@yuzhouu/canvas-kit 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +145 -0
- package/_vendor/canvas-core/bindings/BindingUtil.ts +92 -0
- package/_vendor/canvas-core/bindings/BindingUtilRegistry.ts +22 -0
- package/_vendor/canvas-core/bindings/arrow/arrowBindingHelpers.ts +52 -0
- package/_vendor/canvas-core/contextMenu.ts +85 -0
- package/_vendor/canvas-core/editor/Editor.ts +2614 -0
- package/_vendor/canvas-core/editor/HistoryManager.ts +83 -0
- package/_vendor/canvas-core/editor/StateNode.ts +162 -0
- package/_vendor/canvas-core/editor/camera.ts +6 -0
- package/_vendor/canvas-core/editor/clipboard.ts +478 -0
- package/_vendor/canvas-core/editor/commands.ts +833 -0
- package/_vendor/canvas-core/editor/edgeScroll.ts +37 -0
- package/_vendor/canvas-core/editor/editorComputedHelpers.ts +257 -0
- package/_vendor/canvas-core/editor/editorSelectionTransforms.ts +116 -0
- package/_vendor/canvas-core/editor/events.ts +93 -0
- package/_vendor/canvas-core/editor/index.ts +12 -0
- package/_vendor/canvas-core/editor/interactions.ts +101 -0
- package/_vendor/canvas-core/editor/internal.ts +22 -0
- package/_vendor/canvas-core/editor/managers/BindingManager.ts +262 -0
- package/_vendor/canvas-core/editor/managers/CameraManager.ts +147 -0
- package/_vendor/canvas-core/editor/managers/ClipboardManager.ts +32 -0
- package/_vendor/canvas-core/editor/managers/DocumentSnapshotManager.ts +256 -0
- package/_vendor/canvas-core/editor/managers/InputsManager.ts +319 -0
- package/_vendor/canvas-core/editor/managers/InteractionTransientManager.ts +218 -0
- package/_vendor/canvas-core/editor/managers/QueryManager.ts +837 -0
- package/_vendor/canvas-core/editor/managers/RenderWindowManager.ts +347 -0
- package/_vendor/canvas-core/editor/managers/SelectionLayoutManager.ts +196 -0
- package/_vendor/canvas-core/editor/managers/SelectionManager.ts +98 -0
- package/_vendor/canvas-core/editor/managers/SelectionOverlayManager.ts +308 -0
- package/_vendor/canvas-core/editor/managers/ShapeEditingManager.ts +226 -0
- package/_vendor/canvas-core/editor/managers/ShapeGeometryManager.ts +162 -0
- package/_vendor/canvas-core/editor/managers/ShapeHierarchyManager.ts +797 -0
- package/_vendor/canvas-core/editor/managers/SideEffectManager.ts +83 -0
- package/_vendor/canvas-core/editor/managers/SnapManager.ts +433 -0
- package/_vendor/canvas-core/editor/managers/SnapSnapshotBuilder.ts +430 -0
- package/_vendor/canvas-core/editor/managers/SpatialIndexManager.ts +354 -0
- package/_vendor/canvas-core/editor/managers/ToolManager.ts +81 -0
- package/_vendor/canvas-core/editor/managers/queryState.ts +583 -0
- package/_vendor/canvas-core/editor/managers/selectionGeometry.ts +287 -0
- package/_vendor/canvas-core/editor/managers/shapeOrder.ts +350 -0
- package/_vendor/canvas-core/editor/managers/shapeReparenting.ts +160 -0
- package/_vendor/canvas-core/editor/managers/snapCalculations.ts +412 -0
- package/_vendor/canvas-core/editor/managers/snapGeometry.ts +202 -0
- package/_vendor/canvas-core/editor/managers/snapIndicators.ts +403 -0
- package/_vendor/canvas-core/editor/managers/snapTypes.ts +122 -0
- package/_vendor/canvas-core/editor/queries/shapeTransforms.ts +171 -0
- package/_vendor/canvas-core/editor/renderWindow.ts +439 -0
- package/_vendor/canvas-core/editor/resizeGeometry.ts +654 -0
- package/_vendor/canvas-core/editor/selection.ts +489 -0
- package/_vendor/canvas-core/editor/selectionHandleInteractions.ts +43 -0
- package/_vendor/canvas-core/editor/selectionOverlay.ts +567 -0
- package/_vendor/canvas-core/exports/integrity.ts +174 -0
- package/_vendor/canvas-core/exports/svg.ts +494 -0
- package/_vendor/canvas-core/geometry/Box.ts +80 -0
- package/_vendor/canvas-core/geometry/Ellipse2d.ts +67 -0
- package/_vendor/canvas-core/geometry/Geometry2d.ts +52 -0
- package/_vendor/canvas-core/geometry/Mat.ts +92 -0
- package/_vendor/canvas-core/geometry/Polyline2d.ts +63 -0
- package/_vendor/canvas-core/geometry/Rectangle2d.ts +64 -0
- package/_vendor/canvas-core/geometry/Vec.ts +123 -0
- package/_vendor/canvas-core/geometry/index.ts +13 -0
- package/_vendor/canvas-core/geometry/segments.ts +65 -0
- package/_vendor/canvas-core/index.ts +391 -0
- package/_vendor/canvas-core/plugins/index.ts +5 -0
- package/_vendor/canvas-core/plugins/types.ts +53 -0
- package/_vendor/canvas-core/reactivity.ts +16 -0
- package/_vendor/canvas-core/schema/records.ts +221 -0
- package/_vendor/canvas-core/schema/snapshots.ts +527 -0
- package/_vendor/canvas-core/shapes/ShapeUtil.ts +516 -0
- package/_vendor/canvas-core/shapes/ShapeUtilRegistry.ts +26 -0
- package/_vendor/canvas-core/shapes/capabilities.ts +67 -0
- package/_vendor/canvas-core/shapes/types.ts +20 -0
- package/_vendor/canvas-core/toolbar.ts +148 -0
- package/_vendor/canvas-core/tools/hand/HandTool.ts +75 -0
- package/_vendor/canvas-core/tools/index.ts +2 -0
- package/_vendor/canvas-core/tools/select/SelectTool.ts +33 -0
- package/_vendor/canvas-core/tools/select/selectContainment.ts +104 -0
- package/_vendor/canvas-core/tools/select/selectToolHelpers.ts +801 -0
- package/_vendor/canvas-core/tools/select/states/BrushingState.ts +145 -0
- package/_vendor/canvas-core/tools/select/states/DraggingSelectionHandleInteractionState.ts +116 -0
- package/_vendor/canvas-core/tools/select/states/IdleState.ts +159 -0
- package/_vendor/canvas-core/tools/select/states/PointingCanvasState.ts +41 -0
- package/_vendor/canvas-core/tools/select/states/PointingResizeHandleState.ts +78 -0
- package/_vendor/canvas-core/tools/select/states/PointingRotateHandleState.ts +64 -0
- package/_vendor/canvas-core/tools/select/states/PointingSelectionHandleInteractionState.ts +34 -0
- package/_vendor/canvas-core/tools/select/states/PointingShapeState.ts +140 -0
- package/_vendor/canvas-core/tools/select/states/ResizingState.ts +171 -0
- package/_vendor/canvas-core/tools/select/states/RotatingState.ts +124 -0
- package/_vendor/canvas-core/tools/select/states/SelectStateNode.ts +128 -0
- package/_vendor/canvas-core/tools/select/states/TranslatingState.ts +139 -0
- package/_vendor/canvas-core/tools/select/states/index.ts +11 -0
- package/_vendor/canvas-plugin-api/feature.ts +204 -0
- package/_vendor/canvas-plugin-api/index.ts +142 -0
- package/_vendor/canvas-product-react/api/apiRegistry.ts +35 -0
- package/_vendor/canvas-product-react/api/canvasProductApi.ts +422 -0
- package/_vendor/canvas-product-react/api/index.ts +31 -0
- package/_vendor/canvas-product-react/capabilities/clipboard/clipboardService.ts +36 -0
- package/_vendor/canvas-product-react/capabilities/clipboard/systemClipboard.ts +245 -0
- package/_vendor/canvas-product-react/capabilities/context-menu/systemContextMenu.ts +396 -0
- package/_vendor/canvas-product-react/capabilities/context-menu/useContextMenuSurface.ts +59 -0
- package/_vendor/canvas-product-react/capabilities/export/useSvgExport.ts +156 -0
- package/_vendor/canvas-product-react/capabilities/history/useHistoryControls.ts +23 -0
- package/_vendor/canvas-product-react/capabilities/pages/usePageControls.ts +41 -0
- package/_vendor/canvas-product-react/capabilities/toolbar/systemToolbar.ts +490 -0
- package/_vendor/canvas-product-react/capabilities/toolbar/useToolbarSurfaces.ts +92 -0
- package/_vendor/canvas-product-react/capabilities/zoom/useZoomControls.ts +27 -0
- package/_vendor/canvas-product-react/index.ts +84 -0
- package/_vendor/canvas-product-react/persistence/index.ts +18 -0
- package/_vendor/canvas-product-react/persistence/localSnapshotPersistence.ts +294 -0
- package/_vendor/canvas-product-react/runtime/CanvasProductRoot.tsx +250 -0
- package/_vendor/canvas-product-react/runtime/productDefinition.ts +72 -0
- package/_vendor/canvas-product-react/runtime/productServices.ts +96 -0
- package/_vendor/canvas-react/components/CanvasView.tsx +796 -0
- package/_vendor/canvas-react/components/OverlayLayer.tsx +879 -0
- package/_vendor/canvas-react/components/PageSpace.tsx +34 -0
- package/_vendor/canvas-react/components/ShapeLayer.tsx +428 -0
- package/_vendor/canvas-react/components/index.ts +4 -0
- package/_vendor/canvas-react/hooks/useEditor.ts +11 -0
- package/_vendor/canvas-react/hooks/useValue.ts +14 -0
- package/_vendor/canvas-react/host/CanvasRendererRegistryContext.tsx +14 -0
- package/_vendor/canvas-react/host/contextMenuRegistry.ts +61 -0
- package/_vendor/canvas-react/host/domEventGuards.ts +61 -0
- package/_vendor/canvas-react/host/index.ts +38 -0
- package/_vendor/canvas-react/host/pluginHost.ts +635 -0
- package/_vendor/canvas-react/host/rendererRegistry.tsx +58 -0
- package/_vendor/canvas-react/host/toolbarRegistry.ts +100 -0
- package/_vendor/canvas-react/index.ts +3 -0
- package/_vendor/plugin-arrow/ArrowBindingUtil.ts +239 -0
- package/_vendor/plugin-arrow/ArrowShapeUtil.ts +311 -0
- package/_vendor/plugin-arrow/ArrowTool.ts +208 -0
- package/_vendor/plugin-arrow/arrowGeometry.ts +441 -0
- package/_vendor/plugin-arrow/arrowTerminalBindingResolver.ts +253 -0
- package/_vendor/plugin-arrow/arrowTerminalCommands.ts +230 -0
- package/_vendor/plugin-arrow/canvas.tsx +92 -0
- package/_vendor/plugin-arrow/core.ts +409 -0
- package/_vendor/plugin-arrow/feature.ts +20 -0
- package/_vendor/plugin-arrow/index.ts +28 -0
- package/_vendor/plugin-arrow/selectionHandleInteractions.ts +154 -0
- package/_vendor/plugin-arrow/types.ts +63 -0
- package/_vendor/plugin-draw/DrawShapeRenderer.tsx +60 -0
- package/_vendor/plugin-draw/DrawShapeUtil.ts +372 -0
- package/_vendor/plugin-draw/DrawTool.ts +444 -0
- package/_vendor/plugin-draw/canvas.tsx +24 -0
- package/_vendor/plugin-draw/core.ts +515 -0
- package/_vendor/plugin-draw/drawGeometry.ts +491 -0
- package/_vendor/plugin-draw/drawHitTest.ts +271 -0
- package/_vendor/plugin-draw/drawInterpolation.ts +250 -0
- package/_vendor/plugin-draw/drawPath.ts +550 -0
- package/_vendor/plugin-draw/drawSegments.ts +106 -0
- package/_vendor/plugin-draw/drawSelectionTransform.ts +67 -0
- package/_vendor/plugin-draw/drawShapeGeometry.ts +307 -0
- package/_vendor/plugin-draw/drawStrokeGeometry.ts +454 -0
- package/_vendor/plugin-draw/feature.ts +20 -0
- package/_vendor/plugin-draw/index.ts +31 -0
- package/_vendor/plugin-draw/types.ts +36 -0
- package/_vendor/plugin-eraser/EraserTool.ts +297 -0
- package/_vendor/plugin-eraser/canvas.tsx +616 -0
- package/_vendor/plugin-eraser/core.ts +49 -0
- package/_vendor/plugin-eraser/feature.ts +20 -0
- package/_vendor/plugin-eraser/index.ts +4 -0
- package/_vendor/plugin-frame/FrameShapeUtil.ts +173 -0
- package/_vendor/plugin-frame/FrameTool.ts +225 -0
- package/_vendor/plugin-frame/canvas.tsx +75 -0
- package/_vendor/plugin-frame/core.ts +534 -0
- package/_vendor/plugin-frame/feature.ts +20 -0
- package/_vendor/plugin-frame/index.ts +6 -0
- package/_vendor/plugin-geo/GeoShapeUtil.ts +463 -0
- package/_vendor/plugin-geo/GeoTools.ts +247 -0
- package/_vendor/plugin-geo/canvas.tsx +90 -0
- package/_vendor/plugin-geo/core.ts +495 -0
- package/_vendor/plugin-geo/feature.ts +20 -0
- package/_vendor/plugin-geo/index.ts +7 -0
- package/_vendor/plugin-geo/types.ts +27 -0
- package/_vendor/plugin-group/GroupShapeUtil.ts +59 -0
- package/_vendor/plugin-group/canvas.tsx +59 -0
- package/_vendor/plugin-group/core.ts +40 -0
- package/_vendor/plugin-group/feature.ts +20 -0
- package/_vendor/plugin-group/index.ts +5 -0
- package/_vendor/plugin-image/ImageShapeUtil.ts +95 -0
- package/_vendor/plugin-image/canvas.tsx +212 -0
- package/_vendor/plugin-image/core.ts +542 -0
- package/_vendor/plugin-image/crop.ts +770 -0
- package/_vendor/plugin-image/cropInteractions.ts +350 -0
- package/_vendor/plugin-image/cropSession.ts +38 -0
- package/_vendor/plugin-image/feature.ts +20 -0
- package/_vendor/plugin-image/index.ts +29 -0
- package/_vendor/plugin-image/types.ts +55 -0
- package/_vendor/plugin-image-product-react/ImageImportController.tsx +143 -0
- package/_vendor/plugin-image-product-react/imageAssetService.ts +52 -0
- package/_vendor/plugin-image-product-react/imageBlobStore.ts +315 -0
- package/_vendor/plugin-image-product-react/imageHostActions.ts +438 -0
- package/_vendor/plugin-image-product-react/imageProductApi.ts +88 -0
- package/_vendor/plugin-image-product-react/imageToolbarHostBridge.ts +48 -0
- package/_vendor/plugin-image-product-react/imageToolbarRuntime.ts +21 -0
- package/_vendor/plugin-image-product-react/index.ts +60 -0
- package/_vendor/plugin-image-product-react/snapshotGuards.ts +33 -0
- package/_vendor/plugin-image-product-react/svgImageAssetInliner.ts +54 -0
- package/_vendor/plugin-image-product-react/useImageImport.ts +64 -0
- package/_vendor/plugin-laser/LaserTool.ts +154 -0
- package/_vendor/plugin-laser/canvas.tsx +645 -0
- package/_vendor/plugin-laser/core.ts +49 -0
- package/_vendor/plugin-laser/feature.ts +20 -0
- package/_vendor/plugin-laser/index.ts +4 -0
- package/_vendor/plugin-text/ArrowLabelBindingUtil.ts +336 -0
- package/_vendor/plugin-text/GeoLabelBindingUtil.ts +391 -0
- package/_vendor/plugin-text/TextShapeSvgExporter.ts +101 -0
- package/_vendor/plugin-text/TextShapeUtil.ts +284 -0
- package/_vendor/plugin-text/TextTool.ts +228 -0
- package/_vendor/plugin-text/canvas.tsx +115 -0
- package/_vendor/plugin-text/core.ts +422 -0
- package/_vendor/plugin-text/feature.ts +20 -0
- package/_vendor/plugin-text/host/RichTextEditingBox.tsx +144 -0
- package/_vendor/plugin-text/host/RichTextToolbar.tsx +266 -0
- package/_vendor/plugin-text/host/TiptapTextEditor.tsx +191 -0
- package/_vendor/plugin-text/host/richTextRendering.tsx +49 -0
- package/_vendor/plugin-text/host/textDomSelection.ts +93 -0
- package/_vendor/plugin-text/host/textEditingBoxStyle.ts +36 -0
- package/_vendor/plugin-text/host/textEntryFocus.ts +307 -0
- package/_vendor/plugin-text/host/textFontLoader.ts +149 -0
- package/_vendor/plugin-text/host/textMeasurement.ts +258 -0
- package/_vendor/plugin-text/host/textMeasurementSync.ts +77 -0
- package/_vendor/plugin-text/host/textRangeSelection.ts +134 -0
- package/_vendor/plugin-text/host/textShapeRendererRuntime.tsx +89 -0
- package/_vendor/plugin-text/host/textShapeSync.ts +173 -0
- package/_vendor/plugin-text/host/tiptapTextEditorController.ts +148 -0
- package/_vendor/plugin-text/index.ts +18 -0
- package/_vendor/plugin-text/richTextUtils.ts +259 -0
- package/_vendor/plugin-text/textInteractions.ts +366 -0
- package/_vendor/plugin-text/textLabelHelpers.ts +235 -0
- package/_vendor/plugin-text/textShapeHelpers.ts +123 -0
- package/_vendor/plugin-text/textStyle.ts +96 -0
- package/_vendor/plugin-text/textTypes.ts +48 -0
- package/_vendor/signals/atom.ts +74 -0
- package/_vendor/signals/computed.ts +132 -0
- package/_vendor/signals/core.ts +130 -0
- package/_vendor/signals/effect.ts +117 -0
- package/_vendor/signals/index.ts +9 -0
- package/_vendor/signals/types.ts +17 -0
- package/_vendor/store/Store.ts +171 -0
- package/_vendor/store/diff.ts +229 -0
- package/_vendor/store/history.ts +177 -0
- package/_vendor/store/index.ts +32 -0
- package/_vendor/store/types.ts +37 -0
- package/api/canvasKitApi.d.ts +29 -0
- package/index.css +3 -0
- package/index.d.ts +16 -0
- package/index.mjs +1 -0
- package/internal-modules.d.ts +187 -0
- package/package.json +48 -0
- package/product/Shell.d.ts +19 -0
- package/product/chromeContext.d.ts +70 -0
- package/product/createCanvasKitEditor.d.ts +6 -0
- package/product/index.d.ts +7 -0
- package/product/persistence.d.ts +3 -0
- package/product/product.d.ts +22 -0
- package/product/useCanvasClipboardShortcuts.d.ts +6 -0
- package/product-iyBbEPIs.mjs +4 -0
- package/product.mjs +1 -0
- package/standard/extensions.d.ts +1 -0
- package/standard/index.d.ts +3 -0
- package/standard/standardApi.d.ts +91 -0
- package/standard-BmrxwqpJ.mjs +44 -0
- package/standard.mjs +1 -0
|
@@ -0,0 +1,837 @@
|
|
|
1
|
+
import { atom, computed, type Atom, type Signal } from "@land/signals";
|
|
2
|
+
import type { StoreChange } from "@land/store";
|
|
3
|
+
import { Box } from "../../geometry/Box";
|
|
4
|
+
import { Mat } from "../../geometry/Mat";
|
|
5
|
+
import { Vec } from "../../geometry/Vec";
|
|
6
|
+
import {
|
|
7
|
+
type BindingId,
|
|
8
|
+
type BindingRecord,
|
|
9
|
+
type EditorRecord,
|
|
10
|
+
type PageId,
|
|
11
|
+
type ShapeId,
|
|
12
|
+
type ShapeParentId,
|
|
13
|
+
type ShapeRecord,
|
|
14
|
+
} from "../../schema/records";
|
|
15
|
+
import type { Editor } from "../Editor";
|
|
16
|
+
import { getEditorStore } from "../internal";
|
|
17
|
+
import {
|
|
18
|
+
getShapeGeometry,
|
|
19
|
+
getShapePositionedToPageTransform,
|
|
20
|
+
type ShapeGeometry,
|
|
21
|
+
} from "../queries/shapeTransforms";
|
|
22
|
+
import {
|
|
23
|
+
areShapeIdArraysEqual,
|
|
24
|
+
buildQueryState,
|
|
25
|
+
compareBindings,
|
|
26
|
+
getRemovedShapeIdsFromQueryStateChange,
|
|
27
|
+
type QueryState,
|
|
28
|
+
updateQueryStateFromStoreChange,
|
|
29
|
+
} from "./queryState";
|
|
30
|
+
import {
|
|
31
|
+
createSelectionGeometrySignals,
|
|
32
|
+
type SelectionGeometrySnapshot,
|
|
33
|
+
} from "./selectionGeometry";
|
|
34
|
+
|
|
35
|
+
interface ShapeQuerySignals {
|
|
36
|
+
bounds: Signal<Box | undefined>;
|
|
37
|
+
geometry: Signal<ShapeGeometry | undefined>;
|
|
38
|
+
positionedToPageTransform: Signal<Mat | undefined>;
|
|
39
|
+
pageBounds: Signal<Box | undefined>;
|
|
40
|
+
clipAncestorIds: Signal<ShapeId[]>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type ShapeContainment = "inside" | "intersecting" | "outside";
|
|
44
|
+
|
|
45
|
+
function areBoxesEqual(
|
|
46
|
+
a: Box | undefined | null,
|
|
47
|
+
b: Box | undefined | null,
|
|
48
|
+
): boolean {
|
|
49
|
+
if (a === b) return true;
|
|
50
|
+
if (!a || !b) return a === b;
|
|
51
|
+
return a.x === b.x && a.y === b.y && a.w === b.w && a.h === b.h;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function areVecsEqual(a: Vec | undefined | null, b: Vec | undefined | null) {
|
|
55
|
+
if (a === b) return true;
|
|
56
|
+
if (!a || !b) return a === b;
|
|
57
|
+
return a.x === b.x && a.y === b.y;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function areVecArraysEqual(a: Vec[], b: Vec[]): boolean {
|
|
61
|
+
if (a === b) return true;
|
|
62
|
+
if (a.length !== b.length) return false;
|
|
63
|
+
for (let i = 0; i < a.length; i += 1) {
|
|
64
|
+
const left = a[i];
|
|
65
|
+
const right = b[i];
|
|
66
|
+
if (!left || !right || !areVecsEqual(left, right)) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function areMatsEqual(a: Mat | undefined, b: Mat | undefined): boolean {
|
|
74
|
+
if (a === b) return true;
|
|
75
|
+
if (!a || !b) return a === b;
|
|
76
|
+
return (
|
|
77
|
+
a.a === b.a &&
|
|
78
|
+
a.b === b.b &&
|
|
79
|
+
a.c === b.c &&
|
|
80
|
+
a.d === b.d &&
|
|
81
|
+
a.e === b.e &&
|
|
82
|
+
a.f === b.f
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function areShapeGeometriesEqual(
|
|
87
|
+
a: ShapeGeometry | undefined,
|
|
88
|
+
b: ShapeGeometry | undefined,
|
|
89
|
+
): boolean {
|
|
90
|
+
if (a === b) return true;
|
|
91
|
+
if (!a || !b) return a === b;
|
|
92
|
+
return (
|
|
93
|
+
areBoxesEqual(a.bounds, b.bounds) &&
|
|
94
|
+
areVecsEqual(a.center, b.center) &&
|
|
95
|
+
areVecArraysEqual(a.corners, b.corners) &&
|
|
96
|
+
areVecArraysEqual(a.outlineVertices, b.outlineVertices)
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function cloneBox(box: Box | undefined | null): Box | undefined | null {
|
|
101
|
+
return box ? box.clone() : box;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function cloneVec(vec: Vec | undefined | null): Vec | undefined | null {
|
|
105
|
+
return vec ? vec.clone() : vec;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function cloneVecArray(points: Vec[] | undefined): Vec[] | undefined {
|
|
109
|
+
return points?.map((point) => point.clone());
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function cloneMat(transform: Mat | undefined): Mat | undefined {
|
|
113
|
+
return transform
|
|
114
|
+
? new Mat(
|
|
115
|
+
transform.a,
|
|
116
|
+
transform.b,
|
|
117
|
+
transform.c,
|
|
118
|
+
transform.d,
|
|
119
|
+
transform.e,
|
|
120
|
+
transform.f,
|
|
121
|
+
)
|
|
122
|
+
: undefined;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function cloneShapeGeometry(
|
|
126
|
+
geometry: ShapeGeometry | undefined,
|
|
127
|
+
): ShapeGeometry | undefined {
|
|
128
|
+
if (!geometry) {
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
bounds: geometry.bounds.clone(),
|
|
134
|
+
center: geometry.center.clone(),
|
|
135
|
+
corners: geometry.corners.map((point) => point.clone()),
|
|
136
|
+
outlineVertices: geometry.outlineVertices.map((point) => point.clone()),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function unionBoxes(boxes: readonly Box[]): Box | null {
|
|
141
|
+
if (boxes.length === 0) {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
let minX = Infinity;
|
|
146
|
+
let minY = Infinity;
|
|
147
|
+
let maxX = -Infinity;
|
|
148
|
+
let maxY = -Infinity;
|
|
149
|
+
for (const box of boxes) {
|
|
150
|
+
minX = Math.min(minX, box.minX);
|
|
151
|
+
minY = Math.min(minY, box.minY);
|
|
152
|
+
maxX = Math.max(maxX, box.maxX);
|
|
153
|
+
maxY = Math.max(maxY, box.maxY);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return new Box(minX, minY, maxX - minX, maxY - minY);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function isValidBox(box: Box): boolean {
|
|
160
|
+
return (
|
|
161
|
+
Number.isFinite(box.x) &&
|
|
162
|
+
Number.isFinite(box.y) &&
|
|
163
|
+
Number.isFinite(box.w) &&
|
|
164
|
+
Number.isFinite(box.h) &&
|
|
165
|
+
box.w >= 0 &&
|
|
166
|
+
box.h >= 0
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function transformBox(bounds: Box, transform: Mat): Box {
|
|
171
|
+
return Box.FromPoints(
|
|
172
|
+
[
|
|
173
|
+
new Vec(bounds.minX, bounds.minY),
|
|
174
|
+
new Vec(bounds.maxX, bounds.minY),
|
|
175
|
+
new Vec(bounds.maxX, bounds.maxY),
|
|
176
|
+
new Vec(bounds.minX, bounds.maxY),
|
|
177
|
+
].map((point) => transform.applyToPoint(point)),
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function intersectBoxes(a: Box, b: Box): Box | null {
|
|
182
|
+
const minX = Math.max(a.minX, b.minX);
|
|
183
|
+
const minY = Math.max(a.minY, b.minY);
|
|
184
|
+
const maxX = Math.min(a.maxX, b.maxX);
|
|
185
|
+
const maxY = Math.min(a.maxY, b.maxY);
|
|
186
|
+
if (maxX < minX || maxY < minY) {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
return new Box(minX, minY, maxX - minX, maxY - minY);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export class QueryManager {
|
|
193
|
+
readonly stateSignal: Signal<QueryState>;
|
|
194
|
+
readonly currentPageShapeIdsSignal: Signal<ShapeId[]>;
|
|
195
|
+
readonly currentPageShapesSignal: Signal<ShapeRecord[]>;
|
|
196
|
+
readonly selectionGeometrySignal: Signal<SelectionGeometrySnapshot>;
|
|
197
|
+
readonly selectionPageOutlineVerticesSignal: Signal<Vec[]>;
|
|
198
|
+
readonly selectionPageBoundsSignal: Signal<Box | null>;
|
|
199
|
+
readonly selectionFrameBoundsSignal: Signal<Box | null>;
|
|
200
|
+
readonly selectionHasRotationSignal: Signal<boolean>;
|
|
201
|
+
readonly selectionRotationSignal: Signal<number>;
|
|
202
|
+
readonly selectionRotationCenterSignal: Signal<Vec | null>;
|
|
203
|
+
readonly selectionHasCompatibleRotationSignal: Signal<boolean>;
|
|
204
|
+
readonly selectionToolbarAnchorPagePointSignal: Signal<Vec | null>;
|
|
205
|
+
|
|
206
|
+
private readonly stateAtom: Atom<QueryState>;
|
|
207
|
+
private readonly shapeQuerySignals = new Map<ShapeId, ShapeQuerySignals>();
|
|
208
|
+
|
|
209
|
+
constructor(private readonly editor: Editor) {
|
|
210
|
+
const store = getEditorStore(this.editor);
|
|
211
|
+
this.stateAtom = atom<QueryState>(
|
|
212
|
+
"editor.query.state",
|
|
213
|
+
buildQueryState(Array.from(store.recordsSignal.get().values())),
|
|
214
|
+
);
|
|
215
|
+
this.stateSignal = this.stateAtom;
|
|
216
|
+
store.listen((change) => {
|
|
217
|
+
this.handleStoreChange(change);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
this.currentPageShapeIdsSignal = computed<ShapeId[]>(
|
|
221
|
+
"editor.query.currentPageShapeIds",
|
|
222
|
+
() => this.getShapeIdsForPageSnapshot(this.editor.getCurrentPageId()),
|
|
223
|
+
{ isEqual: areShapeIdArraysEqual },
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
this.currentPageShapesSignal = computed<ShapeRecord[]>(
|
|
227
|
+
"editor.query.currentPageShapes",
|
|
228
|
+
() =>
|
|
229
|
+
this.currentPageShapeIdsSignal
|
|
230
|
+
.get()
|
|
231
|
+
.map((id) => this.editor.getShapeSignal(id).get())
|
|
232
|
+
.filter((shape): shape is ShapeRecord => !!shape),
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
const selectionGeometrySignals = createSelectionGeometrySignals(
|
|
236
|
+
this.editor,
|
|
237
|
+
(shapeId) => this.getShapeGeometrySnapshot(shapeId),
|
|
238
|
+
);
|
|
239
|
+
this.selectionGeometrySignal = selectionGeometrySignals.geometry;
|
|
240
|
+
this.selectionPageOutlineVerticesSignal =
|
|
241
|
+
selectionGeometrySignals.pageOutlineVertices;
|
|
242
|
+
this.selectionPageBoundsSignal = selectionGeometrySignals.pageBounds;
|
|
243
|
+
this.selectionFrameBoundsSignal = selectionGeometrySignals.frameBounds;
|
|
244
|
+
this.selectionRotationSignal = selectionGeometrySignals.rotation;
|
|
245
|
+
this.selectionHasRotationSignal = selectionGeometrySignals.hasRotation;
|
|
246
|
+
this.selectionRotationCenterSignal =
|
|
247
|
+
selectionGeometrySignals.rotationCenter;
|
|
248
|
+
this.selectionHasCompatibleRotationSignal =
|
|
249
|
+
selectionGeometrySignals.hasCompatibleRotation;
|
|
250
|
+
this.selectionToolbarAnchorPagePointSignal =
|
|
251
|
+
selectionGeometrySignals.toolbarAnchorPagePoint;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
getShapeIdsForPage(pageId: PageId): ShapeId[] {
|
|
255
|
+
return [...this.getShapeIdsForPageSnapshot(pageId)];
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
getShapesForPage(pageId: PageId): ShapeRecord[] {
|
|
259
|
+
return this.getShapeIdsForPageSnapshot(pageId)
|
|
260
|
+
.map((id) => this.editor.getShape(id))
|
|
261
|
+
.filter((shape): shape is ShapeRecord => !!shape);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
getChildShapeIds(parentId: ShapeParentId): ShapeId[] {
|
|
265
|
+
return [...this.getChildShapeIdsSnapshot(parentId)];
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
getChildShapes(parentId: ShapeParentId): ShapeRecord[] {
|
|
269
|
+
return this.getChildShapeIdsSnapshot(parentId)
|
|
270
|
+
.map((id) => this.editor.getShape(id))
|
|
271
|
+
.filter((shape): shape is ShapeRecord => !!shape);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
getDescendantShapeIds(parentId: ShapeParentId): ShapeId[] {
|
|
275
|
+
const descendantIds: ShapeId[] = [];
|
|
276
|
+
const visit = (currentParentId: ShapeParentId) => {
|
|
277
|
+
for (const childId of this.getChildShapeIdsSnapshot(currentParentId)) {
|
|
278
|
+
descendantIds.push(childId);
|
|
279
|
+
visit(childId);
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
visit(parentId);
|
|
284
|
+
return descendantIds;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
getPageIdForShape(shapeId: ShapeId): PageId | null {
|
|
288
|
+
return this.stateSignal.get().pageIdByShape.get(shapeId) ?? null;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
getPageIdForShapeParent(parentId: ShapeParentId): PageId | null {
|
|
292
|
+
if (parentId.startsWith("page:")) {
|
|
293
|
+
return this.editor.getPage(parentId as PageId) ? (parentId as PageId) : null;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return this.getPageIdForShape(parentId as ShapeId);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
getDescendantPageBounds(parentId: ShapeParentId): Box | null {
|
|
300
|
+
const bounds: Box[] = [];
|
|
301
|
+
for (const descendantId of this.getDescendantShapeIds(parentId)) {
|
|
302
|
+
const descendantBounds = this.getShapePageBounds(descendantId);
|
|
303
|
+
if (descendantBounds) {
|
|
304
|
+
bounds.push(descendantBounds);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return unionBoxes(bounds);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
getShapeSubtreePageBounds(shapeId: ShapeId): Box | null {
|
|
312
|
+
const bounds: Box[] = [];
|
|
313
|
+
const shapeBounds = this.getShapePageBounds(shapeId);
|
|
314
|
+
if (shapeBounds) {
|
|
315
|
+
bounds.push(shapeBounds);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
for (const descendantId of this.getDescendantShapeIds(shapeId)) {
|
|
319
|
+
const descendantBounds = this.getShapePageBounds(descendantId);
|
|
320
|
+
if (descendantBounds) {
|
|
321
|
+
bounds.push(descendantBounds);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return unionBoxes(bounds);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
getShapeContainment(
|
|
329
|
+
containerId: ShapeId,
|
|
330
|
+
shapeId: ShapeId,
|
|
331
|
+
): ShapeContainment | null {
|
|
332
|
+
const container = this.editor.getShape(containerId);
|
|
333
|
+
if (!container) {
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const containerGeometry = this.getShapeGeometrySnapshot(containerId);
|
|
338
|
+
const shapeGeometry = this.getShapeGeometrySnapshot(shapeId);
|
|
339
|
+
if (!containerGeometry || !shapeGeometry) {
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const containerUtil = this.editor.getShapeUtil(container);
|
|
344
|
+
const containerLocalBounds = containerUtil.getLocalBounds(container);
|
|
345
|
+
const pageToContainerLocal =
|
|
346
|
+
containerUtil.getLocalToPageTransform(container).invert();
|
|
347
|
+
|
|
348
|
+
const isInside =
|
|
349
|
+
shapeGeometry.outlineVertices.length > 0 &&
|
|
350
|
+
shapeGeometry.outlineVertices.every((point) =>
|
|
351
|
+
containerLocalBounds.containsPoint(
|
|
352
|
+
pageToContainerLocal.applyToPoint(point),
|
|
353
|
+
),
|
|
354
|
+
);
|
|
355
|
+
if (isInside) {
|
|
356
|
+
return "inside";
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return containerGeometry.bounds.intersects(shapeGeometry.bounds)
|
|
360
|
+
? "intersecting"
|
|
361
|
+
: "outside";
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
getContainingShapeParentId(
|
|
365
|
+
shapeIds: Iterable<ShapeId>,
|
|
366
|
+
): ShapeParentId | null {
|
|
367
|
+
const selectedIds = Array.from(new Set(shapeIds));
|
|
368
|
+
if (selectedIds.length === 0) {
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const selectedShapes = selectedIds
|
|
373
|
+
.map((id) => this.editor.getShape(id))
|
|
374
|
+
.filter((shape): shape is ShapeRecord => !!shape);
|
|
375
|
+
if (selectedShapes.length !== selectedIds.length) {
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const pageId = this.getPageIdForShape(selectedShapes[0]!.id);
|
|
380
|
+
if (!pageId) {
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (
|
|
385
|
+
selectedShapes.some((shape) => this.getPageIdForShape(shape.id) !== pageId)
|
|
386
|
+
) {
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const blockedParentIds = new Set<ShapeId>();
|
|
391
|
+
for (const shape of selectedShapes) {
|
|
392
|
+
blockedParentIds.add(shape.id);
|
|
393
|
+
for (const descendantId of this.getDescendantShapeIds(shape.id)) {
|
|
394
|
+
blockedParentIds.add(descendantId);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
let bestShape: ShapeRecord | null = null;
|
|
399
|
+
let bestArea = Infinity;
|
|
400
|
+
let bestPageIndex = -1;
|
|
401
|
+
const pageShapeIds =
|
|
402
|
+
this.stateSignal.get().pageShapeIdsByPage.get(pageId) ?? [];
|
|
403
|
+
for (let pageIndex = 0; pageIndex < pageShapeIds.length; pageIndex += 1) {
|
|
404
|
+
const candidateId = pageShapeIds[pageIndex]!;
|
|
405
|
+
if (blockedParentIds.has(candidateId)) {
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const candidate = this.editor.getShape(candidateId);
|
|
410
|
+
if (!candidate) {
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const candidateUtil = this.editor.getShapeUtil(candidate);
|
|
415
|
+
if (!canUseAutomaticContainment(candidate, candidateUtil)) {
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const canReceiveAll = selectedShapes.every((shape) =>
|
|
420
|
+
this.editor.canUseShapeParent(candidate.id, shape.id),
|
|
421
|
+
);
|
|
422
|
+
if (!canReceiveAll) {
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const containsAll = selectedShapes.every(
|
|
427
|
+
(shape) => this.getShapeContainment(candidate.id, shape.id) === "inside",
|
|
428
|
+
);
|
|
429
|
+
if (!containsAll) {
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const candidateBounds = this.getShapePageBounds(candidate.id);
|
|
434
|
+
if (!candidateBounds) {
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const area = candidateBounds.w * candidateBounds.h;
|
|
439
|
+
if (
|
|
440
|
+
!bestShape ||
|
|
441
|
+
area < bestArea ||
|
|
442
|
+
(area === bestArea && pageIndex > bestPageIndex)
|
|
443
|
+
) {
|
|
444
|
+
bestShape = candidate;
|
|
445
|
+
bestArea = area;
|
|
446
|
+
bestPageIndex = pageIndex;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
return bestShape?.id ?? pageId;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
getContainingParentIdForShapeRecord(shape: ShapeRecord): ShapeParentId | null {
|
|
454
|
+
const pageId = this.getPageIdForShapeParent(shape.parentId);
|
|
455
|
+
if (!pageId) {
|
|
456
|
+
return null;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
const shapeGeometry = getShapeGeometry(shape, this.editor.getShapeUtil(shape));
|
|
460
|
+
let bestShape: ShapeRecord | null = null;
|
|
461
|
+
let bestArea = Infinity;
|
|
462
|
+
let bestPageIndex = -1;
|
|
463
|
+
const pageShapeIds =
|
|
464
|
+
this.stateSignal.get().pageShapeIdsByPage.get(pageId) ?? [];
|
|
465
|
+
|
|
466
|
+
for (let pageIndex = 0; pageIndex < pageShapeIds.length; pageIndex += 1) {
|
|
467
|
+
const candidate = this.editor.getShape(pageShapeIds[pageIndex]!);
|
|
468
|
+
if (!candidate || candidate.id === shape.id) {
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
const candidateUtil = this.editor.getShapeUtil(candidate);
|
|
473
|
+
if (!canUseAutomaticContainment(candidate, candidateUtil)) {
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
if (!this.editor.canUseShapeParent(candidate.id, shape.id)) {
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const candidateBounds = this.getShapePageBounds(candidate.id);
|
|
481
|
+
if (!candidateBounds) {
|
|
482
|
+
continue;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const candidateLocalBounds = candidateUtil.getLocalBounds(candidate);
|
|
486
|
+
const pageToCandidateLocal =
|
|
487
|
+
candidateUtil.getLocalToPageTransform(candidate).invert();
|
|
488
|
+
const containsShape =
|
|
489
|
+
shapeGeometry.outlineVertices.length > 0 &&
|
|
490
|
+
shapeGeometry.outlineVertices.every((point) =>
|
|
491
|
+
candidateLocalBounds.containsPoint(
|
|
492
|
+
pageToCandidateLocal.applyToPoint(point),
|
|
493
|
+
),
|
|
494
|
+
);
|
|
495
|
+
if (!containsShape) {
|
|
496
|
+
continue;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
const area = candidateBounds.w * candidateBounds.h;
|
|
500
|
+
if (
|
|
501
|
+
!bestShape ||
|
|
502
|
+
area < bestArea ||
|
|
503
|
+
(area === bestArea && pageIndex > bestPageIndex)
|
|
504
|
+
) {
|
|
505
|
+
bestShape = candidate;
|
|
506
|
+
bestArea = area;
|
|
507
|
+
bestPageIndex = pageIndex;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
return bestShape?.id ?? pageId;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
getShapeClipAncestorIds(shapeId: ShapeId): ShapeId[] {
|
|
515
|
+
return [...this.getShapeClipAncestorIdsSignal(shapeId).get()];
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
getShapeClipAncestorIdsSignal(shapeId: ShapeId): Signal<ShapeId[]> {
|
|
519
|
+
return this.getShapeQuerySignals(shapeId).clipAncestorIds;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
isPagePointInShapeClip(shapeId: ShapeId, point: Vec): boolean {
|
|
523
|
+
const shape = this.editor.getShape(shapeId);
|
|
524
|
+
if (!shape) {
|
|
525
|
+
return false;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
for (const ancestor of this.getShapeClipAncestorsForShape(
|
|
529
|
+
shape,
|
|
530
|
+
(id) => this.editor.getShape(id),
|
|
531
|
+
)) {
|
|
532
|
+
const util = this.editor.getShapeUtil(ancestor);
|
|
533
|
+
const clipBounds = util.getChildClipLocalBounds(ancestor);
|
|
534
|
+
if (!clipBounds || !isValidBox(clipBounds)) {
|
|
535
|
+
continue;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
const localPoint = util
|
|
539
|
+
.getLocalToPageTransform(ancestor)
|
|
540
|
+
.invert()
|
|
541
|
+
.applyToPoint(point);
|
|
542
|
+
if (!clipBounds.containsPoint(localPoint)) {
|
|
543
|
+
return false;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
return true;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
getShapeClippedPageBounds(shapeId: ShapeId): Box | null {
|
|
551
|
+
let bounds = this.getShapePageBounds(shapeId);
|
|
552
|
+
if (!bounds) {
|
|
553
|
+
return null;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
for (const ancestor of this.getShapeClipAncestors(shapeId)) {
|
|
557
|
+
const clipBounds = this.getShapeChildClipPageBounds(ancestor);
|
|
558
|
+
if (!clipBounds) {
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
const nextBounds = intersectBoxes(bounds, clipBounds);
|
|
563
|
+
if (!nextBounds) {
|
|
564
|
+
return null;
|
|
565
|
+
}
|
|
566
|
+
bounds = nextBounds;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
return bounds;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
private getShapeClipAncestors(shapeId: ShapeId): ShapeRecord[] {
|
|
573
|
+
const shape = this.editor.getShape(shapeId);
|
|
574
|
+
return shape
|
|
575
|
+
? this.getShapeClipAncestorsForShape(shape, (id) =>
|
|
576
|
+
this.editor.getShape(id),
|
|
577
|
+
)
|
|
578
|
+
: [];
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
private getShapeClipAncestorsForShape(
|
|
582
|
+
shape: ShapeRecord,
|
|
583
|
+
getShape: (id: ShapeId) => ShapeRecord | undefined,
|
|
584
|
+
): ShapeRecord[] {
|
|
585
|
+
const ancestors: ShapeRecord[] = [];
|
|
586
|
+
const seen = new Set<ShapeId>();
|
|
587
|
+
let parentId = shape.parentId;
|
|
588
|
+
|
|
589
|
+
while (parentId.startsWith("shape:")) {
|
|
590
|
+
const ancestorId = parentId as ShapeId;
|
|
591
|
+
if (seen.has(ancestorId)) {
|
|
592
|
+
break;
|
|
593
|
+
}
|
|
594
|
+
seen.add(ancestorId);
|
|
595
|
+
|
|
596
|
+
const ancestor = getShape(ancestorId);
|
|
597
|
+
if (!ancestor) {
|
|
598
|
+
break;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
const clipBounds = this.editor
|
|
602
|
+
.getShapeUtil(ancestor)
|
|
603
|
+
.getChildClipLocalBounds(ancestor);
|
|
604
|
+
if (clipBounds && isValidBox(clipBounds)) {
|
|
605
|
+
ancestors.push(ancestor);
|
|
606
|
+
}
|
|
607
|
+
parentId = ancestor.parentId;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
return ancestors.reverse();
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
private getShapeChildClipPageBounds(shape: ShapeRecord): Box | null {
|
|
614
|
+
const util = this.editor.getShapeUtil(shape);
|
|
615
|
+
const clipBounds = util.getChildClipLocalBounds(shape);
|
|
616
|
+
if (!clipBounds || !isValidBox(clipBounds)) {
|
|
617
|
+
return null;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
return transformBox(clipBounds, util.getLocalToPageTransform(shape));
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
private getShapeIdsForPageSnapshot(pageId: PageId): ShapeId[] {
|
|
624
|
+
return this.stateSignal.get().pageShapeIdsByPage.get(pageId) ?? [];
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
private getChildShapeIdsSnapshot(parentId: ShapeParentId): ShapeId[] {
|
|
628
|
+
return this.stateSignal.get().childShapeIdsByParent.get(parentId) ?? [];
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
private handleStoreChange(change: StoreChange<EditorRecord>): void {
|
|
632
|
+
for (const shapeId of getRemovedShapeIdsFromQueryStateChange(change)) {
|
|
633
|
+
this.shapeQuerySignals.delete(shapeId);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
const store = getEditorStore(this.editor);
|
|
637
|
+
this.stateAtom.update((previous) => {
|
|
638
|
+
return updateQueryStateFromStoreChange(previous, change, (id) =>
|
|
639
|
+
store.get(id),
|
|
640
|
+
);
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
private getShapeQuerySignals(shapeId: ShapeId): ShapeQuerySignals {
|
|
645
|
+
let signals = this.shapeQuerySignals.get(shapeId);
|
|
646
|
+
if (!signals) {
|
|
647
|
+
const bounds = computed<Box | undefined>(
|
|
648
|
+
`editor.query.shapeBounds.${shapeId}`,
|
|
649
|
+
() => {
|
|
650
|
+
const shape = this.editor.getShapeSignal(shapeId).get();
|
|
651
|
+
if (!shape) {
|
|
652
|
+
return undefined;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
return this.editor.getShapeUtil(shape).getBounds(shape);
|
|
656
|
+
},
|
|
657
|
+
{ isEqual: areBoxesEqual },
|
|
658
|
+
);
|
|
659
|
+
const geometry = computed<ShapeGeometry | undefined>(
|
|
660
|
+
`editor.query.shapeGeometry.${shapeId}`,
|
|
661
|
+
() => {
|
|
662
|
+
const shape = this.editor.getShapeSignal(shapeId).get();
|
|
663
|
+
if (!shape) {
|
|
664
|
+
return undefined;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
return getShapeGeometry(shape, this.editor.getShapeUtil(shape));
|
|
668
|
+
},
|
|
669
|
+
{ isEqual: areShapeGeometriesEqual },
|
|
670
|
+
);
|
|
671
|
+
const positionedToPageTransform = computed<Mat | undefined>(
|
|
672
|
+
`editor.query.shapePositionedToPageTransform.${shapeId}`,
|
|
673
|
+
() => {
|
|
674
|
+
const shape = this.editor.getShapeSignal(shapeId).get();
|
|
675
|
+
if (!shape) {
|
|
676
|
+
return undefined;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
return getShapePositionedToPageTransform(
|
|
680
|
+
shape,
|
|
681
|
+
this.editor.getShapeUtil(shape),
|
|
682
|
+
);
|
|
683
|
+
},
|
|
684
|
+
{ isEqual: areMatsEqual },
|
|
685
|
+
);
|
|
686
|
+
const pageBounds = computed<Box | undefined>(
|
|
687
|
+
`editor.query.shapePageBounds.${shapeId}`,
|
|
688
|
+
() => geometry.get()?.bounds,
|
|
689
|
+
{ isEqual: areBoxesEqual },
|
|
690
|
+
);
|
|
691
|
+
const clipAncestorIds = computed<ShapeId[]>(
|
|
692
|
+
`editor.query.shapeClipAncestorIds.${shapeId}`,
|
|
693
|
+
() => {
|
|
694
|
+
const shape = this.editor.getShapeSignal(shapeId).get();
|
|
695
|
+
if (!shape) {
|
|
696
|
+
return [];
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
return this.getShapeClipAncestorsForShape(shape, (id) =>
|
|
700
|
+
this.editor.getShapeSignal(id).get(),
|
|
701
|
+
).map((ancestor) => ancestor.id);
|
|
702
|
+
},
|
|
703
|
+
{ isEqual: areShapeIdArraysEqual },
|
|
704
|
+
);
|
|
705
|
+
|
|
706
|
+
signals = {
|
|
707
|
+
bounds,
|
|
708
|
+
geometry,
|
|
709
|
+
positionedToPageTransform,
|
|
710
|
+
pageBounds,
|
|
711
|
+
clipAncestorIds,
|
|
712
|
+
};
|
|
713
|
+
this.shapeQuerySignals.set(shapeId, signals);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
return signals;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
private getShapeGeometrySnapshot(shapeId: ShapeId): ShapeGeometry | undefined {
|
|
720
|
+
return this.getShapeQuerySignals(shapeId).geometry.get();
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
getShapePageBoundsSignal(shapeId: ShapeId): Signal<Box | undefined> {
|
|
724
|
+
return this.getShapeQuerySignals(shapeId).pageBounds;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
getShapeGeometry(shapeId: ShapeId): ShapeGeometry | undefined {
|
|
728
|
+
return cloneShapeGeometry(this.getShapeGeometrySnapshot(shapeId));
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
getShapePositionedToPageTransform(shapeId: ShapeId): Mat | undefined {
|
|
732
|
+
return cloneMat(
|
|
733
|
+
this.getShapeQuerySignals(shapeId).positionedToPageTransform.get(),
|
|
734
|
+
);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
getShapePageBounds(shapeId: ShapeId): Box | undefined {
|
|
738
|
+
return cloneBox(this.getShapePageBoundsSignal(shapeId).get()) ?? undefined;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
getShapeBounds(shapeId: ShapeId): Box | undefined {
|
|
742
|
+
return cloneBox(this.getShapeQuerySignals(shapeId).bounds.get()) ?? undefined;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
getShapePageCenter(shapeId: ShapeId): Vec | undefined {
|
|
746
|
+
return cloneVec(this.getShapeGeometrySnapshot(shapeId)?.center) ?? undefined;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
getShapePageCorners(shapeId: ShapeId): Vec[] | undefined {
|
|
750
|
+
return cloneVecArray(this.getShapeGeometrySnapshot(shapeId)?.corners);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
getShapePageOutlineVertices(shapeId: ShapeId): Vec[] | undefined {
|
|
754
|
+
return cloneVecArray(
|
|
755
|
+
this.getShapeGeometrySnapshot(shapeId)?.outlineVertices,
|
|
756
|
+
);
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
getSelectionPageOutlineVertices(): Vec[] {
|
|
760
|
+
return cloneVecArray(this.selectionPageOutlineVerticesSignal.get()) ?? [];
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
getSelectionPageBounds(): Box | null {
|
|
764
|
+
return cloneBox(this.selectionPageBoundsSignal.get()) ?? null;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
getSelectionFrameBounds(): Box | null {
|
|
768
|
+
return cloneBox(this.selectionFrameBoundsSignal.get()) ?? null;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
getSelectionRotation(): number {
|
|
772
|
+
return this.selectionRotationSignal.get();
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
getSelectionHasRotation(): boolean {
|
|
776
|
+
return this.selectionHasRotationSignal.get();
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
getSelectionRotationCenter(): Vec | null {
|
|
780
|
+
return cloneVec(this.selectionRotationCenterSignal.get()) ?? null;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
getSelectionHasCompatibleRotation(): boolean {
|
|
784
|
+
return this.selectionHasCompatibleRotationSignal.get();
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
getSelectionToolbarAnchorPagePoint(): Vec | null {
|
|
788
|
+
return cloneVec(this.selectionToolbarAnchorPagePointSignal.get()) ?? null;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
getBinding(id: BindingId): BindingRecord | undefined {
|
|
792
|
+
return this.stateSignal.get().bindingById.get(id);
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
getBindings(): BindingRecord[] {
|
|
796
|
+
return Array.from(this.stateSignal.get().bindingById.values()).sort(
|
|
797
|
+
compareBindings,
|
|
798
|
+
);
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
getBindingIdsFromShape(shapeId: ShapeId): BindingId[] {
|
|
802
|
+
return this.stateSignal.get().bindingIdsByFromShape.get(shapeId) ?? [];
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
getBindingIdsToShape(shapeId: ShapeId): BindingId[] {
|
|
806
|
+
return this.stateSignal.get().bindingIdsByToShape.get(shapeId) ?? [];
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
getBindingIdsForShape(shapeId: ShapeId): BindingId[] {
|
|
810
|
+
return this.stateSignal.get().relatedBindingIdsByShape.get(shapeId) ?? [];
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
getBindingsFromShape(shapeId: ShapeId): BindingRecord[] {
|
|
814
|
+
return this.getBindingIdsFromShape(shapeId)
|
|
815
|
+
.map((id) => this.getBinding(id))
|
|
816
|
+
.filter((binding): binding is BindingRecord => !!binding);
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
getBindingsToShape(shapeId: ShapeId): BindingRecord[] {
|
|
820
|
+
return this.getBindingIdsToShape(shapeId)
|
|
821
|
+
.map((id) => this.getBinding(id))
|
|
822
|
+
.filter((binding): binding is BindingRecord => !!binding);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
getBindingsForShape(shapeId: ShapeId): BindingRecord[] {
|
|
826
|
+
return this.getBindingIdsForShape(shapeId)
|
|
827
|
+
.map((id) => this.getBinding(id))
|
|
828
|
+
.filter((binding): binding is BindingRecord => !!binding);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
function canUseAutomaticContainment(
|
|
833
|
+
shape: ShapeRecord,
|
|
834
|
+
util: ReturnType<Editor["getShapeUtil"]>,
|
|
835
|
+
): boolean {
|
|
836
|
+
return util.canReceiveChildren(shape) && !util.shouldTransformDescendants(shape);
|
|
837
|
+
}
|