move some more stuff around
This commit is contained in:
parent
400efff3a1
commit
99180e63ce
18 changed files with 32 additions and 32 deletions
|
|
@ -1,7 +1,7 @@
|
|||
import { memo } from "react";
|
||||
import { Arrow, ArrowPart } from "../../statecharts/concrete_syntax";
|
||||
import { ArcDirection, euclideanDistance } from "./geometry";
|
||||
import { CORNER_HELPER_RADIUS } from "./parameters";
|
||||
import { ArcDirection, euclideanDistance } from "../../util/geometry";
|
||||
import { CORNER_HELPER_RADIUS } from "../parameters";
|
||||
import { arraysEqual } from "@/util/util";
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Diamond, RectSide } from "@/statecharts/concrete_syntax";
|
||||
import { rountangleMinSize } from "./VisualEditor";
|
||||
import { Vec2D } from "./geometry";
|
||||
import { Vec2D } from "../../util/geometry";
|
||||
import { RectHelper } from "./RectHelpers";
|
||||
import { memo } from "react";
|
||||
import { arraysEqual } from "@/util/util";
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { memo } from "react";
|
||||
import { Vec2D } from "./geometry";
|
||||
import { HISTORY_RADIUS } from "./parameters";
|
||||
import { Vec2D } from "../../util/geometry";
|
||||
import { HISTORY_RADIUS } from "../parameters";
|
||||
|
||||
export const HistorySVG = memo(function HistorySVG(props: {uid: string, topLeft: Vec2D, kind: "shallow"|"deep", selected: boolean, highlight: boolean}) {
|
||||
const text = props.kind === "shallow" ? "H" : "H*";
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { memo } from "react";
|
||||
import { RectSide } from "../../statecharts/concrete_syntax";
|
||||
import { Vec2D } from "./geometry";
|
||||
import { CORNER_HELPER_OFFSET, CORNER_HELPER_RADIUS } from "./parameters";
|
||||
import { Vec2D } from "../../util/geometry";
|
||||
import { CORNER_HELPER_OFFSET, CORNER_HELPER_RADIUS } from "../parameters";
|
||||
|
||||
function lineGeometryProps(size: Vec2D): [RectSide, object][] {
|
||||
return [
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { memo } from "react";
|
||||
import { Rountangle, RectSide } from "../../statecharts/concrete_syntax";
|
||||
import { ROUNTANGLE_RADIUS } from "./parameters";
|
||||
import { ROUNTANGLE_RADIUS } from "../parameters";
|
||||
import { RectHelper } from "./RectHelpers";
|
||||
import { rountangleMinSize } from "./VisualEditor";
|
||||
import { arraysEqual } from "@/util/util";
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ import { ClipboardEvent, Dispatch, memo, ReactElement, SetStateAction, useCallba
|
|||
import { Statechart } from "../../statecharts/abstract_syntax";
|
||||
import { Arrow, ArrowPart, Diamond, History, Rountangle, RectSide, Text } from "../../statecharts/concrete_syntax";
|
||||
import { parseStatechart, TraceableError } from "../../statecharts/parser";
|
||||
import { ArcDirection, Line2D, Rect2D, Vec2D, addV2D, arcDirection, area, getBottomSide, getLeftSide, getRightSide, getTopSide, isEntirelyWithin, normalizeRect, scaleV2D, subtractV2D, transformLine, transformRect } from "./geometry";
|
||||
import { MIN_ROUNTANGLE_SIZE } from "./parameters";
|
||||
import { getBBoxInSvgCoords } from "./svg_helper";
|
||||
import { ArcDirection, Rect2D, Vec2D, addV2D, arcDirection, area, isEntirelyWithin, normalizeRect, scaleV2D, subtractV2D, transformLine, transformRect } from "../../util/geometry";
|
||||
import { MIN_ROUNTANGLE_SIZE } from "../parameters";
|
||||
import { getBBoxInSvgCoords } from "../../util/svg_helper";
|
||||
import { ArrowSVG } from "./ArrowSVG";
|
||||
import { RountangleSVG } from "./RountangleSVG";
|
||||
import { TextSVG } from "./TextSVG";
|
||||
|
|
@ -52,14 +52,6 @@ type Selectable = RountangleSelectable | ArrowSelectable | TextSelectable | Hist
|
|||
|
||||
type Selection = Selectable[];
|
||||
|
||||
|
||||
export const sides: [RectSide, (r:Rect2D)=>Line2D][] = [
|
||||
["left", getLeftSide],
|
||||
["top", getTopSide],
|
||||
["right", getRightSide],
|
||||
["bottom", getBottomSide],
|
||||
];
|
||||
|
||||
export type InsertMode = "and"|"or"|"pseudo"|"shallow"|"deep"|"transition"|"text";
|
||||
|
||||
type VisualEditorProps = {
|
||||
|
|
|
|||
|
|
@ -1,198 +0,0 @@
|
|||
import { RectSide } from "../../statecharts/concrete_syntax";
|
||||
|
||||
export type Vec2D = {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
|
||||
export type Rect2D = {
|
||||
topLeft: Vec2D;
|
||||
size: Vec2D;
|
||||
};
|
||||
|
||||
export type Line2D = {
|
||||
start: Vec2D;
|
||||
end: Vec2D;
|
||||
};
|
||||
|
||||
// make sure size is always positive
|
||||
export function normalizeRect(rect: Rect2D) {
|
||||
return {
|
||||
topLeft: {
|
||||
x: rect.size.x < 0 ? (rect.topLeft.x + rect.size.x) : rect.topLeft.x,
|
||||
y: rect.size.y < 0 ? (rect.topLeft.y + rect.size.y) : rect.topLeft.y,
|
||||
},
|
||||
size: {
|
||||
x: rect.size.x < 0 ? -rect.size.x : rect.size.x,
|
||||
y: rect.size.y < 0 ? -rect.size.y : rect.size.y,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function isEntirelyWithin(child: Rect2D, parent: Rect2D) {
|
||||
return (
|
||||
child.topLeft.x >= parent.topLeft.x
|
||||
&& child.topLeft.y >= parent.topLeft.y
|
||||
&& child.topLeft.x + child.size.x <= parent.topLeft.x + parent.size.x
|
||||
&& child.topLeft.y + child.size.y <= parent.topLeft.y + parent.size.y
|
||||
);
|
||||
}
|
||||
|
||||
export function isWithin(p: Vec2D, r: Rect2D) {
|
||||
return (
|
||||
p.x >= r.topLeft.x && p.x <= r.topLeft.x + r.size.x
|
||||
&& p.y >= r.topLeft.y && p.y <= r.topLeft.y + r.size.y
|
||||
);
|
||||
}
|
||||
|
||||
export function addV2D(a: Vec2D, b: Vec2D) {
|
||||
return {
|
||||
x: a.x + b.x,
|
||||
y: a.y + b.y,
|
||||
};
|
||||
}
|
||||
|
||||
export function subtractV2D(a: Vec2D, b: Vec2D) {
|
||||
return {
|
||||
x: a.x - b.x,
|
||||
y: a.y - b.y,
|
||||
};
|
||||
}
|
||||
|
||||
export function scaleV2D(p: Vec2D, scale: number) {
|
||||
return {
|
||||
x: p.x * scale,
|
||||
y: p.y * scale,
|
||||
};
|
||||
}
|
||||
|
||||
export function area(rect: Rect2D) {
|
||||
return rect.size.x * rect.size.y;
|
||||
}
|
||||
|
||||
export function lineBBox(line: Line2D, margin=0): Rect2D {
|
||||
return {
|
||||
topLeft: {
|
||||
x: line.start.x - margin,
|
||||
y: line.start.y - margin,
|
||||
},
|
||||
size: {
|
||||
x: line.end.x - line.start.x + margin*2,
|
||||
y: line.end.y - line.start.y + margin*2,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function transformRect(rect: Rect2D, parts: string[], delta: Vec2D): Rect2D {
|
||||
return {
|
||||
topLeft: {
|
||||
x: parts.includes("left") ? rect.topLeft.x + delta.x : rect.topLeft.x,
|
||||
y: parts.includes("top") ? rect.topLeft.y + delta.y : rect.topLeft.y,
|
||||
},
|
||||
size: {
|
||||
x: /*Math.max(40,*/ rect.size.x
|
||||
+ (parts.includes("right") ? delta.x : 0)
|
||||
- (parts.includes("left") ? delta.x : 0),
|
||||
y: /*Math.max(40,*/ rect.size.y
|
||||
+ (parts.includes("bottom") ? delta.y : 0)
|
||||
- (parts.includes("top") ? delta.y : 0),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function transformLine(line: Line2D, parts: string[], delta: Vec2D): Line2D {
|
||||
return {
|
||||
start: parts.includes("start") ? addV2D(line.start, {x: delta.x, y: delta.y}) : line.start,
|
||||
end: parts.includes("end") ? addV2D(line.end, {x: delta.x, y: delta.y}) : line.end,
|
||||
};
|
||||
}
|
||||
|
||||
// intersection point of two lines
|
||||
// note: point may not be part of the lines
|
||||
// author: ChatGPT
|
||||
export function intersectLines(a: Line2D, b: Line2D): Vec2D | null {
|
||||
const { start: A1, end: A2 } = a;
|
||||
const { start: B1, end: B2 } = b;
|
||||
|
||||
const den =
|
||||
(A1.x - A2.x) * (B1.y - B2.y) - (A1.y - A2.y) * (B1.x - B2.x);
|
||||
|
||||
if (den === 0) return null; // parallel or coincident
|
||||
|
||||
const x =
|
||||
((A1.x * A2.y - A1.y * A2.x) * (B1.x - B2.x) -
|
||||
(A1.x - A2.x) * (B1.x * B2.y - B1.y * B2.x)) /
|
||||
den;
|
||||
|
||||
const y =
|
||||
((A1.x * A2.y - A1.y * A2.x) * (B1.y - B2.y) -
|
||||
(A1.y - A2.y) * (B1.x * B2.y - B1.y * B2.x)) /
|
||||
den;
|
||||
|
||||
return { x, y };
|
||||
}
|
||||
|
||||
export function euclideanDistance(a: Vec2D, b: Vec2D): number {
|
||||
const diffX = a.x - b.x;
|
||||
const diffY = a.y - b.y;
|
||||
return Math.hypot(diffX, diffY);
|
||||
// return Math.sqrt(diffX*diffX + diffY*diffY);
|
||||
}
|
||||
|
||||
export function getLeftSide(rect: Rect2D): Line2D {
|
||||
return {
|
||||
start: rect.topLeft,
|
||||
end: {x: rect.topLeft.x, y: rect.topLeft.y + rect.size.y},
|
||||
};
|
||||
}
|
||||
export function getTopSide(rect: Rect2D): Line2D {
|
||||
return {
|
||||
start: rect.topLeft,
|
||||
end: { x: rect.topLeft.x + rect.size.x, y: rect.topLeft.y },
|
||||
};
|
||||
}
|
||||
export function getRightSide(rect: Rect2D): Line2D {
|
||||
return {
|
||||
start: { x: rect.topLeft.x + rect.size.x, y: rect.topLeft.y },
|
||||
end: { x: rect.topLeft.x + rect.size.x, y: rect.topLeft.y + rect.size.y },
|
||||
};
|
||||
}
|
||||
export function getBottomSide(rect: Rect2D): Line2D {
|
||||
return {
|
||||
start: { x: rect.topLeft.x, y: rect.topLeft.y + rect.size.y },
|
||||
end: { x: rect.topLeft.x + rect.size.x, y: rect.topLeft.y + rect.size.y },
|
||||
};
|
||||
}
|
||||
|
||||
export type ArcDirection = "no" | "cw" | "ccw";
|
||||
|
||||
export function arcDirection(start: RectSide, end: RectSide): ArcDirection {
|
||||
if (start === end) {
|
||||
if (start === "left" || start === "top") {
|
||||
return "ccw";
|
||||
}
|
||||
else {
|
||||
return "cw";
|
||||
}
|
||||
}
|
||||
const both = [start, end];
|
||||
if (both.includes("top") && both.includes("bottom")) {
|
||||
return "no";
|
||||
}
|
||||
if (both.includes("left") && both.includes("right")) {
|
||||
return "no";
|
||||
}
|
||||
if (start === "top" && end === "left") {
|
||||
return "ccw";
|
||||
}
|
||||
if (start === "left" && end === "bottom") {
|
||||
return "ccw";
|
||||
}
|
||||
if (start === "bottom" && end === "right") {
|
||||
return "ccw";
|
||||
}
|
||||
if (start === "right" && end === "top") {
|
||||
return "ccw";
|
||||
}
|
||||
return "cw";
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
|
||||
export const ARROW_SNAP_THRESHOLD = 20;
|
||||
export const TEXT_SNAP_THRESHOLD = 40;
|
||||
|
||||
export const ROUNTANGLE_RADIUS = 20;
|
||||
export const MIN_ROUNTANGLE_SIZE = { x: ROUNTANGLE_RADIUS*2, y: ROUNTANGLE_RADIUS*2 };
|
||||
|
||||
// those hoverable green transparent circles in the corners of rountangles:
|
||||
export const CORNER_HELPER_OFFSET = 4;
|
||||
export const CORNER_HELPER_RADIUS = 16;
|
||||
|
||||
export const HISTORY_RADIUS = 20;
|
||||
|
||||
|
||||
export const ZOOM_STEP = 1.25;
|
||||
export const ZOOM_MIN = (1/ZOOM_STEP)**6;
|
||||
export const ZOOM_MAX = ZOOM_STEP**6;
|
||||
30
src/App/VisualEditor/shortcut_handler.ts
Normal file
30
src/App/VisualEditor/shortcut_handler.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import { Dispatch, SetStateAction } from "react";
|
||||
import { InsertMode } from "./VisualEditor";
|
||||
|
||||
export function getKeyHandler(setMode: Dispatch<SetStateAction<InsertMode>>) {
|
||||
return function onKeyDown(e: KeyboardEvent) {
|
||||
if (!e.ctrlKey) {
|
||||
if (e.key === "a") {
|
||||
setMode("and");
|
||||
}
|
||||
if (e.key === "o") {
|
||||
setMode("or");
|
||||
}
|
||||
if (e.key === "p") {
|
||||
setMode("pseudo");
|
||||
}
|
||||
if (e.key === "t") {
|
||||
setMode("transition");
|
||||
}
|
||||
if (e.key === "x") {
|
||||
setMode("text");
|
||||
}
|
||||
if (e.key === "h") {
|
||||
setMode(oldMode => {
|
||||
if (oldMode === "shallow") return "deep";
|
||||
return "shallow";
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
import { Rect2D } from "./geometry";
|
||||
|
||||
// author: ChatGPT
|
||||
export function getBBoxInSvgCoords(el: SVGGraphicsElement, svg: SVGSVGElement): Rect2D {
|
||||
const b = el.getBBox();
|
||||
const m = el.getCTM()!;
|
||||
const toSvg = (x: number, y: number) => {
|
||||
const p = svg.createSVGPoint();
|
||||
p.x = x; p.y = y;
|
||||
return p.matrixTransform(m);
|
||||
};
|
||||
const pts = [
|
||||
toSvg(b.x, b.y),
|
||||
toSvg(b.x + b.width, b.y),
|
||||
toSvg(b.x, b.y + b.height),
|
||||
toSvg(b.x + b.width, b.y + b.height)
|
||||
];
|
||||
const xs = pts.map(p => p.x);
|
||||
const ys = pts.map(p => p.y);
|
||||
return {
|
||||
topLeft: {
|
||||
x: Math.min(...xs),
|
||||
y: Math.min(...ys),
|
||||
},
|
||||
size: {
|
||||
x: Math.max(...xs) - Math.min(...xs),
|
||||
y: Math.max(...ys) - Math.min(...ys),
|
||||
},
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue