when selecting text, highlight rountangle to which text belongs
This commit is contained in:
parent
da0e56e17c
commit
41f34ab65e
3 changed files with 28 additions and 4 deletions
|
|
@ -4,7 +4,7 @@ import { ArcDirection, Line2D, Rect2D, Vec2D, addV2D, arcDirection, area, euclid
|
||||||
import "./VisualEditor.css";
|
import "./VisualEditor.css";
|
||||||
|
|
||||||
import { getBBoxInSvgCoords } from "./svg_helper";
|
import { getBBoxInSvgCoords } from "./svg_helper";
|
||||||
import { VisualEditorState, Rountangle, emptyState, Arrow, ArrowPart, RountanglePart, findNearestRountangleSide, findNearestArrow, Text } from "./editor_types";
|
import { VisualEditorState, Rountangle, emptyState, Arrow, ArrowPart, RountanglePart, findNearestRountangleSide, findNearestArrow, Text, findRountangle } from "./editor_types";
|
||||||
import { parseStatechart } from "./parser";
|
import { parseStatechart } from "./parser";
|
||||||
import { CORNER_HELPER_OFFSET, CORNER_HELPER_RADIUS, MIN_ROUNTANGLE_SIZE, ROUNTANGLE_RADIUS } from "./parameters";
|
import { CORNER_HELPER_OFFSET, CORNER_HELPER_RADIUS, MIN_ROUNTANGLE_SIZE, ROUNTANGLE_RADIUS } from "./parameters";
|
||||||
|
|
||||||
|
|
@ -401,6 +401,7 @@ export function VisualEditor() {
|
||||||
const side2ArrowMap = new Map<string, Set<["start"|"end", string]>>();
|
const side2ArrowMap = new Map<string, Set<["start"|"end", string]>>();
|
||||||
const text2ArrowMap = new Map<string,string>();
|
const text2ArrowMap = new Map<string,string>();
|
||||||
const arrow2TextMap = new Map<string,string[]>();
|
const arrow2TextMap = new Map<string,string[]>();
|
||||||
|
const text2RountangleMap = new Map<string, string>();
|
||||||
for (const arrow of state.arrows) {
|
for (const arrow of state.arrows) {
|
||||||
const startSide = findNearestRountangleSide(arrow, "start", state.rountangles);
|
const startSide = findNearestRountangleSide(arrow, "start", state.rountangles);
|
||||||
const endSide = findNearestRountangleSide(arrow, "end", state.rountangles);
|
const endSide = findNearestRountangleSide(arrow, "end", state.rountangles);
|
||||||
|
|
@ -421,17 +422,26 @@ export function VisualEditor() {
|
||||||
for (const text of state.texts) {
|
for (const text of state.texts) {
|
||||||
const nearestArrow = findNearestArrow(text.topLeft, state.arrows);
|
const nearestArrow = findNearestArrow(text.topLeft, state.arrows);
|
||||||
if (nearestArrow) {
|
if (nearestArrow) {
|
||||||
|
// prioritize text belonging to arrows:
|
||||||
text2ArrowMap.set(text.uid, nearestArrow.uid);
|
text2ArrowMap.set(text.uid, nearestArrow.uid);
|
||||||
const textsOfArrow = arrow2TextMap.get(nearestArrow.uid) || [];
|
const textsOfArrow = arrow2TextMap.get(nearestArrow.uid) || [];
|
||||||
textsOfArrow.push(text.uid);
|
textsOfArrow.push(text.uid);
|
||||||
arrow2TextMap.set(nearestArrow.uid, textsOfArrow);
|
arrow2TextMap.set(nearestArrow.uid, textsOfArrow);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
// no arrow, then the text belongs to the rountangle it is in
|
||||||
|
const rountangle = findRountangle(text.topLeft, state.rountangles);
|
||||||
|
if (rountangle) {
|
||||||
|
text2RountangleMap.set(text.uid, rountangle.uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// for visual feedback, when selecting/moving one thing, we also highlight (in green) all the things that belong to the thing we selected.
|
// 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]: RountanglePart[]} = {};
|
||||||
const arrowsToHighlight: {[key: string]: boolean} = {};
|
const arrowsToHighlight: {[key: string]: boolean} = {};
|
||||||
const textsToHighlight: {[key: string]: boolean} = {};
|
const textsToHighlight: {[key: string]: boolean} = {};
|
||||||
|
const rountanglesToHighlight: {[key: string]: boolean} = {};
|
||||||
for (const selected of selection) {
|
for (const selected of selection) {
|
||||||
const sides = arrow2SideMap.get(selected.uid);
|
const sides = arrow2SideMap.get(selected.uid);
|
||||||
if (sides) {
|
if (sides) {
|
||||||
|
|
@ -455,6 +465,10 @@ export function VisualEditor() {
|
||||||
if (arrow2) {
|
if (arrow2) {
|
||||||
arrowsToHighlight[arrow2] = true;
|
arrowsToHighlight[arrow2] = true;
|
||||||
}
|
}
|
||||||
|
const rountangleUid = text2RountangleMap.get(selected.uid)
|
||||||
|
if (rountangleUid) {
|
||||||
|
rountanglesToHighlight[rountangleUid] = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const rootErrors = errors.filter(([uid]) => uid === "root").map(err=>err[1]);
|
const rootErrors = errors.filter(([uid]) => uid === "root").map(err=>err[1]);
|
||||||
|
|
@ -484,7 +498,7 @@ export function VisualEditor() {
|
||||||
key={rountangle.uid}
|
key={rountangle.uid}
|
||||||
rountangle={rountangle}
|
rountangle={rountangle}
|
||||||
selected={selection.find(r => r.uid === rountangle.uid)?.parts || []}
|
selected={selection.find(r => r.uid === rountangle.uid)?.parts || []}
|
||||||
highlight={sidesToHighlight[rountangle.uid] || []}
|
highlight={[...(sidesToHighlight[rountangle.uid] || []), ...(rountanglesToHighlight[rountangle.uid]?["left","right","top","bottom"]:[])]}
|
||||||
errors={errors.filter(([uid,msg])=>uid===rountangle.uid).map(err=>err[1])}
|
errors={errors.filter(([uid,msg])=>uid===rountangle.uid).map(err=>err[1])}
|
||||||
/>)}
|
/>)}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Rect2D, Vec2D, Line2D, euclideanDistance, intersectLines, isWithin, lineBBox } from "./geometry";
|
import { Rect2D, Vec2D, Line2D, euclideanDistance, intersectLines, isWithin, lineBBox, isEntirelyWithin } from "./geometry";
|
||||||
import { ARROW_SNAP_THRESHOLD, TEXT_SNAP_THRESHOLD } from "./parameters";
|
import { ARROW_SNAP_THRESHOLD, TEXT_SNAP_THRESHOLD } from "./parameters";
|
||||||
import { sides } from "./VisualEditor";
|
import { sides } from "./VisualEditor";
|
||||||
|
|
||||||
|
|
@ -106,3 +106,12 @@ export function findNearestArrow(point: Vec2D, candidates: Arrow[]): Arrow | und
|
||||||
|
|
||||||
return best;
|
return best;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// precondition: candidates are sorted from big to small
|
||||||
|
export function findRountangle(point: Vec2D, candidates: Rountangle[]): Rountangle | undefined {
|
||||||
|
for (let i=candidates.length-1; i>=0; i--) {
|
||||||
|
if (isWithin(point, candidates[i])) {
|
||||||
|
return candidates[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,4 +23,5 @@ export function recursiveEnter(state: ConcreteState): RT_ConcreteState {
|
||||||
current_rt: recursiveEnter(currentState),
|
current_rt: recursiveEnter(currentState),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue