From 4f9a546fd1c188e5b7c86fa93a1b4ded762634e5 Mon Sep 17 00:00:00 2001 From: Joeri Exelmans Date: Thu, 23 Oct 2025 23:13:09 +0200 Subject: [PATCH] move the parsing from VisualEditor to App component --- src/App/App.tsx | 22 ++++---- src/App/RTHistory.tsx | 73 +++++++++++++------------ src/App/ShowAST.tsx | 4 +- src/App/TopPanel.tsx | 12 ++-- src/VisualEditor/DiamondSVG.tsx | 5 +- src/VisualEditor/RectHelpers.tsx | 6 +- src/VisualEditor/RountangleSVG.tsx | 6 +- src/VisualEditor/VisualEditor.tsx | 79 ++++++++++++--------------- src/VisualEditor/geometry.ts | 4 +- src/statecharts/concrete_syntax.ts | 6 +- src/statecharts/detect_connections.ts | 6 +- 11 files changed, 107 insertions(+), 116 deletions(-) diff --git a/src/App/App.tsx b/src/App/App.tsx index d45ac56..34a25bb 100644 --- a/src/App/App.tsx +++ b/src/App/App.tsx @@ -1,8 +1,7 @@ import { ReactElement, useCallback, useEffect, useMemo, useRef, useState } from "react"; -import { emptyStatechart, Statechart, Transition } from "../statecharts/abstract_syntax"; import { handleInputEvent, initialize, RuntimeError } from "../statecharts/interpreter"; -import { BigStep, BigStepOutput, RT_Event } from "../statecharts/runtime_types"; +import { BigStep, RT_Event } from "../statecharts/runtime_types"; import { InsertMode, VisualEditor, VisualEditorState } from "../VisualEditor/VisualEditor"; import { getSimTime, getWallClkDelay, TimeMode } from "../statecharts/time"; @@ -13,7 +12,7 @@ import Stack from "@mui/material/Stack"; import Box from "@mui/material/Box"; import { TopPanel } from "./TopPanel"; import { ShowAST, ShowInputEvents, ShowInternalEvents, ShowOutputEvents } from "./ShowAST"; -import { TraceableError } from "../statecharts/parser"; +import { parseStatechart } from "../statecharts/parser"; import { getKeyHandler } from "./shortcut_handler"; import { BottomPanel } from "./BottomPanel"; import { emptyState } from "@/statecharts/concrete_syntax"; @@ -23,6 +22,7 @@ import { DummyPlant } from "@/Plant/Dummy/Dummy"; import { Plant } from "@/Plant/Plant"; import { usePersistentState } from "@/util/persistent_state"; import { RTHistory } from "./RTHistory"; +import { detectConnections } from "@/statecharts/detect_connections"; export type EditHistory = { current: VisualEditorState, @@ -70,10 +70,8 @@ function getPlantState(plant: Plant, trace: TraceItem[], idx: number): T | } export function App() { - const [mode, setMode] = useState("and"); + const [insertMode, setInsertMode] = useState("and"); const [historyState, setHistoryState] = useState({current: emptyState, history: [], future: []}); - const [ast, setAST] = useState(emptyStatechart); - const [errors, setErrors] = useState([]); const [trace, setTrace] = useState(null); const [time, setTime] = useState({kind: "paused", simtime: 0}); const [modal, setModal] = useState(null); @@ -91,6 +89,10 @@ export function App() { const refRightSideBar = useRef(null); + // parse concrete syntax always: + const conns = useMemo(() => detectConnections(editorState), [editorState]); + const [ast, syntaxErrors] = useMemo(() => parseStatechart(editorState, conns), [editorState, conns]); + // append editor state to undo history const makeCheckPoint = useCallback(() => { setHistoryState(historyState => ({ @@ -261,7 +263,7 @@ export function App() { }, []); useEffect(() => { - const onKeyDown = getKeyHandler(setMode); + const onKeyDown = getKeyHandler(setInsertMode); window.addEventListener("keydown", onKeyDown); return () => { window.removeEventListener("keydown", onKeyDown); @@ -322,12 +324,12 @@ export function App() { }} > {/* Below the top bar: Editor */} - + @@ -408,7 +410,7 @@ export function App() { {/* Bottom panel */} - + ; diff --git a/src/App/RTHistory.tsx b/src/App/RTHistory.tsx index 90d8f74..76aceda 100644 --- a/src/App/RTHistory.tsx +++ b/src/App/RTHistory.tsx @@ -1,9 +1,9 @@ -import { Dispatch, Ref, SetStateAction } from "react"; +import { Dispatch, memo, Ref, SetStateAction, useCallback } from "react"; import { Statechart, stateDescription } from "../statecharts/abstract_syntax"; import { BigStep, Environment, Mode, RaisedEvent, RT_Event } from "../statecharts/runtime_types"; import { formatTime } from "./util"; import { TimeMode } from "../statecharts/time"; -import { TraceState } from "./App"; +import { TraceItem, TraceState } from "./App"; type RTHistoryProps = { trace: TraceState|null, @@ -13,52 +13,55 @@ type RTHistoryProps = { } export function RTHistory({trace, setTrace, ast, setTime}: RTHistoryProps) { - function gotoRt(idx: number, timestamp: number) { + const onMouseDown = useCallback((idx: number, timestamp: number) => { setTrace(trace => trace && { ...trace, idx, }); setTime({kind: "paused", simtime: timestamp}); - } + }, [setTrace, setTime]); if (trace === null) { return <>; } return
- {trace.trace.map((item, i) => { - if (item.kind === "bigstep") { - const newStates = item.mode.difference(trace.trace[i-1]?.mode || new Set()); - return
gotoRt(i, item.simtime)}> -
- {formatTime(item.simtime)} -   -
{item.inputEvent || ""}
-
- - - {item.outputEvents.length>0 && <>^ - {item.outputEvents.map((e:RaisedEvent) => {e.name})} - } -
; - } - else { - return
-
- {formatTime(item.simtime)} -   -
{item.inputEvent}
-
-
- {item.error.message} -
-
; - } - })} + {trace.trace.map((item, i) => )}
; } +export const RTHistoryItem = memo(function RTHistoryItem({ast, idx, item, prevItem, active, onMouseDown}: {idx: number, ast: Statechart, item: TraceItem, prevItem?: TraceItem, active: boolean, onMouseDown: (idx: number, timestamp: number) => void}) { + if (item.kind === "bigstep") { + // @ts-ignore + const newStates = item.mode.difference(prevItem?.mode || new Set()); + return
onMouseDown(idx, item.simtime), [idx, item.simtime])}> +
+ {formatTime(item.simtime)} +   +
{item.inputEvent || ""}
+
+ + + {item.outputEvents.length>0 && <>^ + {item.outputEvents.map((e:RaisedEvent) => {e.name})} + } +
; + } + else { + return
+
+ {formatTime(item.simtime)} +   +
{item.inputEvent}
+
+
+ {item.error.message} +
+
; + } +}); + function ShowEnvironment(props: {environment: Environment}) { return
{ diff --git a/src/App/ShowAST.tsx b/src/App/ShowAST.tsx index b0e6139..ac5aa9c 100644 --- a/src/App/ShowAST.tsx +++ b/src/App/ShowAST.tsx @@ -32,7 +32,7 @@ export function ShowAction(props: {action: Action}) { } } -export const ShowAST = memo(function ShowAST(props: {root: ConcreteState | PseudoState, transitions: Map, trace: TraceState | null, highlightActive: Set}) { +export const ShowAST = memo(function ShowASTx(props: {root: ConcreteState | PseudoState}) { const description = stateDescription(props.root); // const outgoing = props.transitions.get(props.root.uid) || []; @@ -40,7 +40,7 @@ export const ShowAST = memo(function ShowAST(props: {root: ConcreteState | Pseud {props.root.kind !== "pseudo" && props.root.children.length>0 &&
    {props.root.children.map(child => - + )}
} diff --git a/src/App/TopPanel.tsx b/src/App/TopPanel.tsx index cf4223d..415e9f6 100644 --- a/src/App/TopPanel.tsx +++ b/src/App/TopPanel.tsx @@ -34,8 +34,8 @@ export type TopPanelProps = { // onRaise: (e: string, p: any) => void, onBack: () => void, // ast: Statechart, - mode: InsertMode, - setMode: Dispatch>, + insertMode: InsertMode, + setInsertMode: Dispatch>, setModal: Dispatch>, zoom: number, setZoom: Dispatch>, @@ -56,7 +56,7 @@ const insertModes: [InsertMode, string, ReactElement, ReactElement][] = [ ["text", "text", <> T , X], ]; -export const TopPanel = memo(function TopPanel({trace, time, setTime, onUndo, onRedo, onInit, onClear, onBack, mode, setMode, setModal, zoom, setZoom, showKeys, setShowKeys, history}: TopPanelProps) { +export const TopPanel = memo(function TopPanel({trace, time, setTime, onUndo, onRedo, onInit, onClear, onBack, insertMode, setInsertMode, setModal, zoom, setZoom, showKeys, setShowKeys, history}: TopPanelProps) { const [displayTime, setDisplayTime] = useState("0.000"); const [timescale, setTimescale] = useState(1); @@ -226,9 +226,9 @@ export const TopPanel = memo(function TopPanel({trace, time, setTime, onUndo, on )}  
diff --git a/src/VisualEditor/DiamondSVG.tsx b/src/VisualEditor/DiamondSVG.tsx index b625e7c..e4d9a5f 100644 --- a/src/VisualEditor/DiamondSVG.tsx +++ b/src/VisualEditor/DiamondSVG.tsx @@ -1,4 +1,4 @@ -import { Diamond, RountanglePart } from "@/statecharts/concrete_syntax"; +import { Diamond, RectSide } from "@/statecharts/concrete_syntax"; import { rountangleMinSize } from "./VisualEditor"; import { Vec2D } from "./geometry"; import { RectHelper } from "./RectHelpers"; @@ -21,8 +21,7 @@ export const DiamondShape = memo(function DiamondShape(props: {size: Vec2D, extr />; }); -export const DiamondSVG = memo(function DiamondSVG(props: { diamond: Diamond; selected: RountanglePart[]; highlight: RountanglePart[]; error?: string; active: boolean; }) { - console.log('render diamond', props.diamond.uid); +export const DiamondSVG = memo(function DiamondSVG(props: { diamond: Diamond; selected: RectSide[]; highlight: RectSide[]; error?: string; active: boolean; }) { const minSize = rountangleMinSize(props.diamond.size); const extraAttrs = { className: '' diff --git a/src/VisualEditor/RectHelpers.tsx b/src/VisualEditor/RectHelpers.tsx index a88d90e..44d4ea9 100644 --- a/src/VisualEditor/RectHelpers.tsx +++ b/src/VisualEditor/RectHelpers.tsx @@ -1,9 +1,9 @@ import { memo } from "react"; -import { RountanglePart } from "../statecharts/concrete_syntax"; +import { RectSide } from "../statecharts/concrete_syntax"; import { Vec2D } from "./geometry"; import { CORNER_HELPER_OFFSET, CORNER_HELPER_RADIUS } from "./parameters"; -function lineGeometryProps(size: Vec2D): [RountanglePart, object][] { +function lineGeometryProps(size: Vec2D): [RectSide, object][] { return [ ["top", {x1: 0, y1: 0, x2: size.x, y2: 0 }], ["right", {x1: size.x, y1: 0, x2: size.x, y2: size.y}], @@ -13,7 +13,7 @@ function lineGeometryProps(size: Vec2D): [RountanglePart, object][] { } // no need to memo() this component, the parent component is already memoized -export const RectHelper = function RectHelper(props: { uid: string, size: Vec2D, selected: RountanglePart[], highlight: string[] }) { +export const RectHelper = function RectHelper(props: { uid: string, size: Vec2D, selected: RectSide[], highlight: string[] }) { const geomProps = lineGeometryProps(props.size); return <> {geomProps.map(([side, ps]) => diff --git a/src/VisualEditor/RountangleSVG.tsx b/src/VisualEditor/RountangleSVG.tsx index 463f713..7659d78 100644 --- a/src/VisualEditor/RountangleSVG.tsx +++ b/src/VisualEditor/RountangleSVG.tsx @@ -1,14 +1,12 @@ import { memo } from "react"; -import { Rountangle, RountanglePart } from "../statecharts/concrete_syntax"; +import { Rountangle, RectSide } from "../statecharts/concrete_syntax"; import { ROUNTANGLE_RADIUS } from "./parameters"; import { RectHelper } from "./RectHelpers"; import { rountangleMinSize } from "./VisualEditor"; import { arraysEqual } from "@/App/util"; -export const RountangleSVG = memo(function RountangleSVG(props: {rountangle: Rountangle; selected: RountanglePart[]; highlight: RountanglePart[]; error?: string; active: boolean; }) { - console.log('render rountangle', props.rountangle.uid); - +export const RountangleSVG = memo(function RountangleSVG(props: {rountangle: Rountangle; selected: RectSide[]; highlight: RectSide[]; error?: string; active: boolean; }) { const { topLeft, size, uid } = props.rountangle; // always draw a rountangle with a minimum size // during resizing, rountangle can be smaller than this size and even have a negative size, but we don't show it diff --git a/src/VisualEditor/VisualEditor.tsx b/src/VisualEditor/VisualEditor.tsx index 56c8cd6..d56751d 100644 --- a/src/VisualEditor/VisualEditor.tsx +++ b/src/VisualEditor/VisualEditor.tsx @@ -1,7 +1,7 @@ import { ClipboardEvent, Dispatch, memo, ReactElement, SetStateAction, useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Statechart } from "../statecharts/abstract_syntax"; -import { Arrow, ArrowPart, Diamond, History, Rountangle, RountanglePart, Text } from "../statecharts/concrete_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"; @@ -11,7 +11,7 @@ import { RountangleSVG } from "./RountangleSVG"; import { TextSVG } from "./TextSVG"; import { DiamondSVG } from "./DiamondSVG"; import { HistorySVG } from "./HistorySVG"; -import { detectConnections } from "../statecharts/detect_connections"; +import { Connections, detectConnections } from "../statecharts/detect_connections"; import "./VisualEditor.css"; import { TraceState } from "@/App/App"; @@ -30,7 +30,7 @@ type SelectingState = Rect2D | null; export type RountangleSelectable = { // kind: "rountangle"; - parts: RountanglePart[]; + parts: RectSide[]; uid: string; } type ArrowSelectable = { @@ -51,7 +51,7 @@ type Selectable = RountangleSelectable | ArrowSelectable | TextSelectable | Hist type Selection = Selectable[]; -export const sides: [RountanglePart, (r:Rect2D)=>Line2D][] = [ +export const sides: [RectSide, (r:Rect2D)=>Line2D][] = [ ["left", getLeftSide], ["top", getTopSide], ["right", getRightSide], @@ -63,12 +63,10 @@ export type InsertMode = "and"|"or"|"pseudo"|"shallow"|"deep"|"transition"|"text type VisualEditorProps = { state: VisualEditorState, setState: Dispatch<(v:VisualEditorState) => VisualEditorState>, - // ast: Statechart, - setAST: Dispatch>, + conns: Connections, + syntaxErrors: TraceableError[], trace: TraceState | null, - errors: TraceableError[], - setErrors: Dispatch>, - mode: InsertMode, + insertMode: InsertMode, highlightActive: Set, highlightTransitions: string[], setModal: Dispatch>, @@ -76,7 +74,7 @@ type VisualEditorProps = { zoom: number; }; -export const VisualEditor = memo(function VisualEditor({state, setState, setAST, trace, errors, setErrors, mode, highlightActive, highlightTransitions, setModal, makeCheckPoint, zoom}: VisualEditorProps) { +export const VisualEditor = memo(function VisualEditor({state, setState, trace, conns, syntaxErrors: errors, insertMode, highlightActive, highlightTransitions, setModal, makeCheckPoint, zoom}: VisualEditorProps) { const [dragging, setDragging] = useState(false); @@ -153,31 +151,22 @@ export const VisualEditor = memo(function VisualEditor({state, setState, setAST, return () => clearTimeout(timeout); }, [state]); - const conns = useMemo(() => detectConnections(state), [state]); - - useEffect(() => { - const [statechart, errors] = parseStatechart(state, conns); - setErrors(errors); - setAST(statechart); - }, [state]) - - function getCurrentPointer(e: {pageX: number, pageY: number}) { + const getCurrentPointer = useCallback((e: {pageX: number, pageY: number}) => { const bbox = refSVG.current!.getBoundingClientRect(); return { x: (e.pageX - bbox.left)/zoom, y: (e.pageY - bbox.top)/zoom, } - } + }, [refSVG.current]); - const onMouseDown = (e: {button: number, target: any, pageX: number, pageY: number}) => { + const onMouseDown = useCallback((e: {button: number, target: any, pageX: number, pageY: number}) => { const currentPointer = getCurrentPointer(e); - if (e.button === 2) { makeCheckPoint(); // ignore selection, middle mouse button always inserts setState(state => { const newID = state.nextID.toString(); - if (mode === "and" || mode === "or") { + if (insertMode === "and" || insertMode === "or") { // insert rountangle return { ...state, @@ -185,13 +174,13 @@ export const VisualEditor = memo(function VisualEditor({state, setState, setAST, uid: newID, topLeft: currentPointer, size: MIN_ROUNTANGLE_SIZE, - kind: mode, + kind: insertMode, }], nextID: state.nextID+1, selection: [{uid: newID, parts: ["bottom", "right"]}], }; } - else if (mode === "pseudo") { + else if (insertMode === "pseudo") { return { ...state, diamonds: [...state.diamonds, { @@ -203,19 +192,19 @@ export const VisualEditor = memo(function VisualEditor({state, setState, setAST, selection: [{uid: newID, parts: ["bottom", "right"]}], }; } - else if (mode === "shallow" || mode === "deep") { + else if (insertMode === "shallow" || insertMode === "deep") { return { ...state, history: [...state.history, { uid: newID, - kind: mode, + kind: insertMode, topLeft: currentPointer, }], nextID: state.nextID+1, selection: [{uid: newID, parts: ["history"]}], } } - else if (mode === "transition") { + else if (insertMode === "transition") { return { ...state, arrows: [...state.arrows, { @@ -227,7 +216,7 @@ export const VisualEditor = memo(function VisualEditor({state, setState, setAST, selection: [{uid: newID, parts: ["end"]}], } } - else if (mode === "text") { + else if (insertMode === "text") { return { ...state, texts: [...state.texts, { @@ -239,7 +228,7 @@ export const VisualEditor = memo(function VisualEditor({state, setState, setAST, selection: [{uid: newID, parts: ["text"]}], } } - throw new Error("unreachable, mode=" + mode); // shut up typescript + throw new Error("unreachable, mode=" + insertMode); // shut up typescript }); setDragging(true); return; @@ -288,9 +277,9 @@ export const VisualEditor = memo(function VisualEditor({state, setState, setAST, size: {x: 0, y: 0}, }); setSelection(() => []); - }; + }, [getCurrentPointer, makeCheckPoint, insertMode, selection]); - const onMouseMove = (e: {pageX: number, pageY: number, movementX: number, movementY: number}) => { + const onMouseMove = useCallback((e: {pageX: number, pageY: number, movementX: number, movementY: number}) => { const currentPointer = getCurrentPointer(e); if (dragging) { // const pointerDelta = subtractV2D(currentPointer, dragging.lastMousePos); @@ -298,7 +287,7 @@ export const VisualEditor = memo(function VisualEditor({state, setState, setAST, setState(state => ({ ...state, rountangles: state.rountangles.map(r => { - const parts = selection.find(selected => selected.uid === r.uid)?.parts || []; + const parts = state.selection.find(selected => selected.uid === r.uid)?.parts || []; if (parts.length === 0) { return r; } @@ -309,7 +298,7 @@ export const VisualEditor = memo(function VisualEditor({state, setState, setAST, }) .toSorted((a,b) => area(b) - area(a)), // sort: smaller rountangles are drawn on top diamonds: state.diamonds.map(d => { - const parts = selection.find(selected => selected.uid === d.uid)?.parts || []; + const parts = state.selection.find(selected => selected.uid === d.uid)?.parts || []; if (parts.length === 0) { return d; } @@ -319,7 +308,7 @@ export const VisualEditor = memo(function VisualEditor({state, setState, setAST, } }), history: state.history.map(h => { - const parts = selection.find(selected => selected.uid === h.uid)?.parts || []; + const parts = state.selection.find(selected => selected.uid === h.uid)?.parts || []; if (parts.length === 0) { return h; } @@ -329,7 +318,7 @@ export const VisualEditor = memo(function VisualEditor({state, setState, setAST, } }), arrows: state.arrows.map(a => { - const parts = selection.find(selected => selected.uid === a.uid)?.parts || []; + const parts = state.selection.find(selected => selected.uid === a.uid)?.parts || []; if (parts.length === 0) { return a; } @@ -339,7 +328,7 @@ export const VisualEditor = memo(function VisualEditor({state, setState, setAST, } }), texts: state.texts.map(t => { - const parts = selection.find(selected => selected.uid === t.uid)?.parts || []; + const parts = state.selection.find(selected => selected.uid === t.uid)?.parts || []; if (parts.length === 0) { return t; } @@ -360,9 +349,9 @@ export const VisualEditor = memo(function VisualEditor({state, setState, setAST, }; }); } - }; + }, [getCurrentPointer, selectingState, dragging]); - const onMouseUp = (e: {target: any, pageX: number, pageY: number}) => { + const onMouseUp = useCallback((e: {target: any, pageX: number, pageY: number}) => { if (dragging) { setDragging(false); // do not persist sizes smaller than 40x40 @@ -424,7 +413,7 @@ export const VisualEditor = memo(function VisualEditor({state, setState, setAST, } } setSelectingState(null); // no longer making a selection - }; + }, [dragging, selectingState, refSVG.current]); function deleteSelection() { setState(state => ({ @@ -499,7 +488,7 @@ export const VisualEditor = memo(function VisualEditor({state, setState, setAST, }, [selectingState, dragging]); // for visual feedback, when selecting/moving one thing, we also highlight (in green) all the things that belong to the thing we selected. - const sidesToHighlight: {[key: string]: RountanglePart[]} = {}; + const sidesToHighlight: {[key: string]: RectSide[]} = {}; const arrowsToHighlight: {[key: string]: boolean} = {}; const textsToHighlight: {[key: string]: boolean} = {}; const rountanglesToHighlight: {[key: string]: boolean} = {}; @@ -716,8 +705,8 @@ export const VisualEditor = memo(function VisualEditor({state, setState, setAST, return r.uid === rountangle.uid)?.parts || []} - highlight={[...(sidesToHighlight[rountangle.uid] || []), ...(rountanglesToHighlight[rountangle.uid]?["left","right","top","bottom"]:[]) as RountanglePart[]]} + selected={selection.find(r => r.uid === rountangle.uid)?.parts as RectSide[] || []} + highlight={[...(sidesToHighlight[rountangle.uid] || []), ...(rountanglesToHighlight[rountangle.uid]?["left","right","top","bottom"]:[]) as RectSide[]]} error={errors .filter(({shapeUid}) => shapeUid === rountangle.uid) .map(({message}) => message).join(', ')} @@ -728,8 +717,8 @@ export const VisualEditor = memo(function VisualEditor({state, setState, setAST, r.uid === diamond.uid)?.parts || []} - highlight={[...(sidesToHighlight[diamond.uid] || []), ...(rountanglesToHighlight[diamond.uid]?["left","right","top","bottom"]:[]) as RountanglePart[]]} + selected={selection.find(r => r.uid === diamond.uid)?.parts as RectSide[] || []} + highlight={[...(sidesToHighlight[diamond.uid] || []), ...(rountanglesToHighlight[diamond.uid]?["left","right","top","bottom"]:[]) as RectSide[]]} error={errors .filter(({shapeUid}) => shapeUid === diamond.uid) .map(({message}) => message).join(', ')} diff --git a/src/VisualEditor/geometry.ts b/src/VisualEditor/geometry.ts index 35405cf..dbb3d1e 100644 --- a/src/VisualEditor/geometry.ts +++ b/src/VisualEditor/geometry.ts @@ -1,4 +1,4 @@ -import { RountanglePart } from "../statecharts/concrete_syntax"; +import { RectSide } from "../statecharts/concrete_syntax"; export type Vec2D = { x: number; @@ -166,7 +166,7 @@ export function getBottomSide(rect: Rect2D): Line2D { export type ArcDirection = "no" | "cw" | "ccw"; -export function arcDirection(start: RountanglePart, end: RountanglePart): ArcDirection { +export function arcDirection(start: RectSide, end: RectSide): ArcDirection { if (start === end) { if (start === "left" || start === "top") { return "ccw"; diff --git a/src/statecharts/concrete_syntax.ts b/src/statecharts/concrete_syntax.ts index 9939df9..d4c6c40 100644 --- a/src/statecharts/concrete_syntax.ts +++ b/src/statecharts/concrete_syntax.ts @@ -28,7 +28,7 @@ export type History = { }; // independently moveable parts of our shapes: -export type RountanglePart = "left" | "top" | "right" | "bottom"; +export type RectSide = "left" | "top" | "right" | "bottom"; export type ArrowPart = "start" | "end"; export const emptyState: VisualEditorState = { @@ -36,9 +36,9 @@ export const emptyState: VisualEditorState = { }; // used to find which rountangle an arrow connects to (src/tgt) -export function findNearestSide(arrow: Line2D, arrowPart: "start" | "end", candidates: (Rountangle|Diamond)[]): {uid: string, part: RountanglePart} | undefined { +export function findNearestSide(arrow: Line2D, arrowPart: "start" | "end", candidates: (Rountangle|Diamond)[]): {uid: string, part: RectSide} | undefined { let best = Infinity; - let bestSide: undefined | {uid: string, part: RountanglePart}; + let bestSide: undefined | {uid: string, part: RectSide}; for (const rountangle of candidates) { for (const [side, getSide] of sides) { const asLine = getSide(rountangle); diff --git a/src/statecharts/detect_connections.ts b/src/statecharts/detect_connections.ts index 2d4cfa4..37a9167 100644 --- a/src/statecharts/detect_connections.ts +++ b/src/statecharts/detect_connections.ts @@ -1,8 +1,8 @@ import { VisualEditorState } from "@/VisualEditor/VisualEditor"; -import { findNearestArrow, findNearestHistory, findNearestSide, findRountangle, RountanglePart } from "./concrete_syntax"; +import { findNearestArrow, findNearestHistory, findNearestSide, findRountangle, RectSide } from "./concrete_syntax"; export type Connections = { - arrow2SideMap: Map, + arrow2SideMap: Map, side2ArrowMap: Map>, text2ArrowMap: Map, arrow2TextMap: Map, @@ -14,7 +14,7 @@ export type Connections = { export function detectConnections(state: VisualEditorState): Connections { // detect what is 'connected' - const arrow2SideMap = new Map(); + const arrow2SideMap = new Map(); const side2ArrowMap = new Map>(); const text2ArrowMap = new Map(); const arrow2TextMap = new Map();