@elixpo/lixsketch 5.6.4 → 5.6.6
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/dist/mcp/index.js +5 -1
- package/dist/mcp/index.js.map +2 -2
- package/dist/mcp/stdio.js +5 -1
- package/dist/mcp/stdio.js.map +2 -2
- package/dist/react/{AIRenderer-SRF7LU5K.js → AIRenderer-R5M2CIDO.js} +13 -6
- package/dist/react/AIRenderer-R5M2CIDO.js.map +7 -0
- package/dist/react/Circle-HMM5PKJ2.js +10 -0
- package/dist/react/{CopyPaste-YUDZ6DHY.js → CopyPaste-P6G35CXO.js} +11 -10
- package/dist/react/{CopyPaste-YUDZ6DHY.js.map → CopyPaste-P6G35CXO.js.map} +1 -1
- package/dist/react/{EventDispatcher-7LAK7GQ7.js → EventDispatcher-M5B4EWHJ.js} +216 -63
- package/dist/react/EventDispatcher-M5B4EWHJ.js.map +7 -0
- package/dist/react/Frame-QEUEYITT.js +11 -0
- package/dist/react/{FreehandStroke-U6GYZOM4.js → FreehandStroke-WXIJKD5I.js} +2 -2
- package/dist/react/{GraphEngine-MM6ALA2N.js → GraphEngine-XH3RPYO4.js} +9 -2
- package/dist/react/GraphEngine-XH3RPYO4.js.map +7 -0
- package/dist/react/IconShape-NHJE4YBY.js +10 -0
- package/dist/react/ImageShape-33VCMSDP.js +10 -0
- package/dist/react/Line-A6FE232Q.js +10 -0
- package/dist/react/{LixScriptParser-JYWJX375.js → LixScriptParser-YS3ZOTGS.js} +6 -2
- package/dist/react/LixScriptParser-YS3ZOTGS.js.map +7 -0
- package/dist/react/{MermaidFlowchartRenderer-PQEC4TMI.js → MermaidFlowchartRenderer-EL3Q2MNW.js} +6 -2
- package/dist/react/MermaidFlowchartRenderer-EL3Q2MNW.js.map +7 -0
- package/dist/react/MermaidSequenceParser-DBYXW23P.js +8 -0
- package/dist/react/{MermaidSequenceRenderer-KCUHO7UI.js → MermaidSequenceRenderer-K4KHHEVC.js} +10 -3
- package/dist/react/MermaidSequenceRenderer-K4KHHEVC.js.map +7 -0
- package/dist/react/{MermaidStructuredRenderer-W7UKN7BM.js → MermaidStructuredRenderer-6LN3WISP.js} +7 -2
- package/dist/react/MermaidStructuredRenderer-6LN3WISP.js.map +7 -0
- package/dist/react/Rectangle-4U4CD2ER.js +10 -0
- package/dist/react/{SceneSerializer-SG6SJN5X.js → SceneSerializer-JEYTYXZQ.js} +120 -46
- package/dist/react/SceneSerializer-JEYTYXZQ.js.map +7 -0
- package/dist/react/{Selection-2TSH3J6P.js → Selection-23MOLHEI.js} +4 -4
- package/dist/react/{SketchEngine-UD2XV2KT.js → SketchEngine-OPJON7TD.js} +26 -26
- package/dist/react/{UndoRedo-IN4SXDG6.js → UndoRedo-QVNRI5OB.js} +8 -2
- package/dist/react/{arrowTool-OYDWJGQK.js → arrowTool-VH3PJPDM.js} +3 -3
- package/dist/react/{chunk-LSHZYN77.js → chunk-2DZHO67H.js} +2 -2
- package/dist/react/{chunk-XOWNCYE4.js → chunk-5NHJJJBZ.js} +3 -3
- package/dist/react/chunk-6AIFXYV6.js +31 -0
- package/dist/react/chunk-6AIFXYV6.js.map +7 -0
- package/dist/react/{chunk-K5RY4EV7.js → chunk-6GPWSPJF.js} +9 -5
- package/dist/react/{chunk-K5RY4EV7.js.map → chunk-6GPWSPJF.js.map} +2 -2
- package/dist/react/{chunk-ASBC5G35.js → chunk-B6CYUZC4.js} +2 -2
- package/dist/react/{chunk-OP56XPWF.js → chunk-DNFPTVIP.js} +2 -2
- package/dist/react/{chunk-XYUFEIZR.js → chunk-DWEFPKDO.js} +2 -2
- package/dist/react/{chunk-GQUZPON4.js → chunk-F56XUE5S.js} +4 -4
- package/dist/react/{chunk-7CM5XQDN.js → chunk-G3DJQY65.js} +20 -36
- package/dist/react/chunk-G3DJQY65.js.map +7 -0
- package/dist/react/{chunk-LJR7EVW2.js → chunk-JEGLUNBC.js} +3 -3
- package/dist/react/{chunk-6GPEWDRB.js → chunk-M7GAB24F.js} +52 -6
- package/dist/react/chunk-M7GAB24F.js.map +7 -0
- package/dist/react/{chunk-VQVEPJSA.js → chunk-MHV5P6TS.js} +2 -1
- package/dist/react/chunk-MHV5P6TS.js.map +7 -0
- package/dist/react/{chunk-PXFPPO3R.js → chunk-NH3NVSWT.js} +18 -2
- package/dist/react/chunk-NH3NVSWT.js.map +7 -0
- package/dist/react/{chunk-XFL4R5ZF.js → chunk-OK4ROGMM.js} +16 -35
- package/dist/react/chunk-OK4ROGMM.js.map +7 -0
- package/dist/react/{chunk-APTOPAOY.js → chunk-SSFEN5CY.js} +3 -3
- package/dist/react/{chunk-RGMDWLRJ.js → chunk-UEL2G6D2.js} +3 -3
- package/dist/react/{chunk-M6TMN76K.js → chunk-ULPIHUVC.js} +3 -3
- package/dist/react/{chunk-FETTJLCV.js → chunk-VZPH7OAM.js} +14 -22
- package/dist/react/chunk-VZPH7OAM.js.map +7 -0
- package/dist/react/{chunk-TKXKJNTX.js → chunk-W7XUY4GR.js} +16 -19
- package/dist/react/chunk-W7XUY4GR.js.map +7 -0
- package/dist/react/{chunk-EOURRJ55.js → chunk-XSE4HQ2D.js} +2 -2
- package/dist/react/{chunk-K5BG5MHY.js → chunk-ZNZAICWY.js} +36 -60
- package/dist/react/chunk-ZNZAICWY.js.map +7 -0
- package/dist/react/{circleTool-LDKGZALF.js → circleTool-2UODOBCV.js} +4 -4
- package/dist/react/{codeTool-4YZO5W7J.js → codeTool-F4RV7SAI.js} +4 -4
- package/dist/react/{frameTool-MXNJEXQQ.js → frameTool-NTGA5BI6.js} +4 -4
- package/dist/react/{freehandTool-26D3FOOX.js → freehandTool-L55EUB5C.js} +4 -4
- package/dist/react/{iconTool-46O5VEUX.js → iconTool-KQQBLGR3.js} +4 -4
- package/dist/react/{imageTool-P2RWZRHO.js → imageTool-LDJ45AD7.js} +4 -4
- package/dist/react/index.js +148 -37
- package/dist/react/index.js.map +4 -4
- package/dist/react/{lineTool-ZMR27U5L.js → lineTool-CSAFD7PW.js} +4 -4
- package/dist/react/{rectangleTool-EJKK6UTX.js → rectangleTool-YPD2SIIZ.js} +4 -4
- package/dist/react/{textTool-S4XXZHZH.js → textTool-VQHMWEFJ.js} +5 -5
- package/package.json +5 -2
- package/src/core/AIRenderer.js +8 -2
- package/src/core/CanvasSpace.js +28 -0
- package/src/core/GraphRenderer.js +8 -3
- package/src/core/LixScriptParser.js +7 -1
- package/src/core/MermaidFlowchartRenderer.js +5 -1
- package/src/core/MermaidSequenceParser.js +1 -0
- package/src/core/MermaidSequenceRenderer.js +8 -3
- package/src/core/MermaidStructuredRenderer.js +6 -1
- package/src/core/SceneSerializer.js +121 -38
- package/src/core/Selection.js +12 -2
- package/src/core/UndoRedo.js +59 -5
- package/src/react/components/AppMenu.jsx +20 -19
- package/src/react/components/canvas/CanvasRulers.jsx +8 -13
- package/src/react/components/canvas/SVGCanvas.jsx +2 -1
- package/src/react/components/canvas/rulerUnits.js +28 -0
- package/src/react/components/sidebars/PaintBucketSidebar.jsx +1 -1
- package/src/react/store/useSketchStore.js +24 -3
- package/src/react/store/useUIStore.js +24 -1
- package/src/react/utils/canvasBackgrounds.js +29 -0
- package/src/shapes/Circle.js +12 -19
- package/src/shapes/Frame.js +38 -69
- package/src/shapes/FreehandStroke.js +24 -1
- package/src/shapes/Rectangle.js +10 -22
- package/src/tools/circleTool.js +3 -27
- package/src/tools/paintBucketTool.js +72 -15
- package/src/tools/rectangleTool.js +7 -23
- package/src/tools/shapeRecognition.js +270 -0
- package/src/tools/shapeRecognitionTool.js +36 -183
- package/dist/react/AIRenderer-SRF7LU5K.js.map +0 -7
- package/dist/react/Circle-6POZCUZD.js +0 -9
- package/dist/react/EventDispatcher-7LAK7GQ7.js.map +0 -7
- package/dist/react/Frame-7D2MXCFF.js +0 -11
- package/dist/react/GraphEngine-MM6ALA2N.js.map +0 -7
- package/dist/react/IconShape-ZADX7Y33.js +0 -10
- package/dist/react/ImageShape-UJQN2HI7.js +0 -10
- package/dist/react/Line-APEULMHE.js +0 -10
- package/dist/react/LixScriptParser-JYWJX375.js.map +0 -7
- package/dist/react/MermaidFlowchartRenderer-PQEC4TMI.js.map +0 -7
- package/dist/react/MermaidSequenceParser-UKG2NSZY.js +0 -8
- package/dist/react/MermaidSequenceRenderer-KCUHO7UI.js.map +0 -7
- package/dist/react/MermaidStructuredRenderer-W7UKN7BM.js.map +0 -7
- package/dist/react/Rectangle-S7XUKWYD.js +0 -9
- package/dist/react/SceneSerializer-SG6SJN5X.js.map +0 -7
- package/dist/react/chunk-6GPEWDRB.js.map +0 -7
- package/dist/react/chunk-7CM5XQDN.js.map +0 -7
- package/dist/react/chunk-FETTJLCV.js.map +0 -7
- package/dist/react/chunk-K5BG5MHY.js.map +0 -7
- package/dist/react/chunk-PXFPPO3R.js.map +0 -7
- package/dist/react/chunk-TKXKJNTX.js.map +0 -7
- package/dist/react/chunk-VQVEPJSA.js.map +0 -7
- package/dist/react/chunk-XFL4R5ZF.js.map +0 -7
- /package/dist/react/{Circle-6POZCUZD.js.map → Circle-HMM5PKJ2.js.map} +0 -0
- /package/dist/react/{Frame-7D2MXCFF.js.map → Frame-QEUEYITT.js.map} +0 -0
- /package/dist/react/{FreehandStroke-U6GYZOM4.js.map → FreehandStroke-WXIJKD5I.js.map} +0 -0
- /package/dist/react/{IconShape-ZADX7Y33.js.map → IconShape-NHJE4YBY.js.map} +0 -0
- /package/dist/react/{ImageShape-UJQN2HI7.js.map → ImageShape-33VCMSDP.js.map} +0 -0
- /package/dist/react/{Line-APEULMHE.js.map → Line-A6FE232Q.js.map} +0 -0
- /package/dist/react/{MermaidSequenceParser-UKG2NSZY.js.map → MermaidSequenceParser-DBYXW23P.js.map} +0 -0
- /package/dist/react/{Rectangle-S7XUKWYD.js.map → Rectangle-4U4CD2ER.js.map} +0 -0
- /package/dist/react/{Selection-2TSH3J6P.js.map → Selection-23MOLHEI.js.map} +0 -0
- /package/dist/react/{SketchEngine-UD2XV2KT.js.map → SketchEngine-OPJON7TD.js.map} +0 -0
- /package/dist/react/{UndoRedo-IN4SXDG6.js.map → UndoRedo-QVNRI5OB.js.map} +0 -0
- /package/dist/react/{arrowTool-OYDWJGQK.js.map → arrowTool-VH3PJPDM.js.map} +0 -0
- /package/dist/react/{chunk-LSHZYN77.js.map → chunk-2DZHO67H.js.map} +0 -0
- /package/dist/react/{chunk-XOWNCYE4.js.map → chunk-5NHJJJBZ.js.map} +0 -0
- /package/dist/react/{chunk-ASBC5G35.js.map → chunk-B6CYUZC4.js.map} +0 -0
- /package/dist/react/{chunk-OP56XPWF.js.map → chunk-DNFPTVIP.js.map} +0 -0
- /package/dist/react/{chunk-XYUFEIZR.js.map → chunk-DWEFPKDO.js.map} +0 -0
- /package/dist/react/{chunk-GQUZPON4.js.map → chunk-F56XUE5S.js.map} +0 -0
- /package/dist/react/{chunk-LJR7EVW2.js.map → chunk-JEGLUNBC.js.map} +0 -0
- /package/dist/react/{chunk-APTOPAOY.js.map → chunk-SSFEN5CY.js.map} +0 -0
- /package/dist/react/{chunk-RGMDWLRJ.js.map → chunk-UEL2G6D2.js.map} +0 -0
- /package/dist/react/{chunk-M6TMN76K.js.map → chunk-ULPIHUVC.js.map} +0 -0
- /package/dist/react/{chunk-EOURRJ55.js.map → chunk-XSE4HQ2D.js.map} +0 -0
- /package/dist/react/{circleTool-LDKGZALF.js.map → circleTool-2UODOBCV.js.map} +0 -0
- /package/dist/react/{codeTool-4YZO5W7J.js.map → codeTool-F4RV7SAI.js.map} +0 -0
- /package/dist/react/{frameTool-MXNJEXQQ.js.map → frameTool-NTGA5BI6.js.map} +0 -0
- /package/dist/react/{freehandTool-26D3FOOX.js.map → freehandTool-L55EUB5C.js.map} +0 -0
- /package/dist/react/{iconTool-46O5VEUX.js.map → iconTool-KQQBLGR3.js.map} +0 -0
- /package/dist/react/{imageTool-P2RWZRHO.js.map → imageTool-LDJ45AD7.js.map} +0 -0
- /package/dist/react/{lineTool-ZMR27U5L.js.map → lineTool-CSAFD7PW.js.map} +0 -0
- /package/dist/react/{rectangleTool-EJKK6UTX.js.map → rectangleTool-YPD2SIIZ.js.map} +0 -0
- /package/dist/react/{textTool-S4XXZHZH.js.map → textTool-VQHMWEFJ.js.map} +0 -0
|
@@ -4,32 +4,32 @@ import {
|
|
|
4
4
|
handleTextMouseDown,
|
|
5
5
|
handleTextMouseMove,
|
|
6
6
|
handleTextMouseUp
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-F56XUE5S.js";
|
|
8
8
|
import {
|
|
9
9
|
handleCodeMouseDown,
|
|
10
10
|
handleCodeMouseMove,
|
|
11
11
|
handleCodeMouseUp
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-ULPIHUVC.js";
|
|
13
13
|
import {
|
|
14
14
|
handleMouseDownImage,
|
|
15
15
|
handleMouseMoveImage,
|
|
16
16
|
handleMouseUpImage
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-JEGLUNBC.js";
|
|
18
18
|
import {
|
|
19
19
|
handleMouseDownIcon,
|
|
20
20
|
handleMouseMoveIcon,
|
|
21
21
|
handleMouseUpIcon
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-SSFEN5CY.js";
|
|
23
23
|
import {
|
|
24
24
|
handleMouseDown as handleMouseDown4,
|
|
25
25
|
handleMouseMove as handleMouseMove4,
|
|
26
26
|
handleMouseUp as handleMouseUp4
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-2DZHO67H.js";
|
|
28
28
|
import {
|
|
29
29
|
handleMouseDown as handleMouseDown5,
|
|
30
30
|
handleMouseMove as handleMouseMove5,
|
|
31
31
|
handleMouseUp as handleMouseUp5
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-5NHJJJBZ.js";
|
|
33
33
|
import {
|
|
34
34
|
handleMultiSelectionMouseDown,
|
|
35
35
|
handleMultiSelectionMouseMove,
|
|
@@ -37,25 +37,25 @@ import {
|
|
|
37
37
|
isMultiSelecting,
|
|
38
38
|
multiSelection,
|
|
39
39
|
removeMultiSelectionRect
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-6GPWSPJF.js";
|
|
41
41
|
import {
|
|
42
42
|
Frame
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-ZNZAICWY.js";
|
|
44
44
|
import {
|
|
45
45
|
handleMouseDownRect,
|
|
46
46
|
handleMouseMoveRect,
|
|
47
47
|
handleMouseUpRect
|
|
48
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-G3DJQY65.js";
|
|
49
49
|
import {
|
|
50
50
|
handleMouseDown as handleMouseDown2,
|
|
51
51
|
handleMouseMove as handleMouseMove2,
|
|
52
52
|
handleMouseUp as handleMouseUp2
|
|
53
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-OK4ROGMM.js";
|
|
54
54
|
import {
|
|
55
55
|
handleMouseDown as handleMouseDown3,
|
|
56
56
|
handleMouseMove as handleMouseMove3,
|
|
57
57
|
handleMouseUp as handleMouseUp3
|
|
58
|
-
} from "./chunk-
|
|
58
|
+
} from "./chunk-UEL2G6D2.js";
|
|
59
59
|
import {
|
|
60
60
|
normalizeWebEmbedUrl
|
|
61
61
|
} from "./chunk-QZI4UIE5.js";
|
|
@@ -65,35 +65,16 @@ import {
|
|
|
65
65
|
handleMouseDown,
|
|
66
66
|
handleMouseMove,
|
|
67
67
|
handleMouseUp
|
|
68
|
-
} from "./chunk-
|
|
68
|
+
} from "./chunk-DWEFPKDO.js";
|
|
69
69
|
import {
|
|
70
70
|
pushCreateAction,
|
|
71
71
|
pushFrameAttachmentAction,
|
|
72
72
|
pushOptionsChangeAction
|
|
73
|
-
} from "./chunk-
|
|
73
|
+
} from "./chunk-M7GAB24F.js";
|
|
74
74
|
|
|
75
|
-
// src/tools/
|
|
76
|
-
var
|
|
77
|
-
var
|
|
78
|
-
var MIN_SCREEN_SAMPLE_PX = 4;
|
|
79
|
-
var MIN_SHAPE_SIZE = 8;
|
|
80
|
-
var drawing = false;
|
|
81
|
-
var points = [];
|
|
82
|
-
var previewPath = null;
|
|
83
|
-
var previewFrame = 0;
|
|
84
|
-
var latestPrediction = null;
|
|
85
|
-
function getThemeStroke() {
|
|
86
|
-
return document.body?.classList.contains("theme-dark") ? "#f7f4ff" : "#211a33";
|
|
87
|
-
}
|
|
88
|
-
function getSVGPoint(event) {
|
|
89
|
-
const viewBox = svg.viewBox.baseVal;
|
|
90
|
-
const rect = svg.getBoundingClientRect();
|
|
91
|
-
return {
|
|
92
|
-
x: viewBox.x + (event.clientX - rect.left) / rect.width * viewBox.width,
|
|
93
|
-
y: viewBox.y + (event.clientY - rect.top) / rect.height * viewBox.height,
|
|
94
|
-
pressure: event.pressure || 0.5
|
|
95
|
-
};
|
|
96
|
-
}
|
|
75
|
+
// src/tools/shapeRecognition.js
|
|
76
|
+
var MIN_SHAPE_SIZE2 = 8;
|
|
77
|
+
var MAX_GESTURE_POINTS = 96;
|
|
97
78
|
function distance(a, b) {
|
|
98
79
|
return Math.hypot(b.x - a.x, b.y - a.y);
|
|
99
80
|
}
|
|
@@ -119,8 +100,8 @@ function orientedBounds(sample, angle) {
|
|
|
119
100
|
x: centerU * cos - centerV * sin,
|
|
120
101
|
y: centerU * sin + centerV * cos
|
|
121
102
|
},
|
|
122
|
-
width: Math.max(
|
|
123
|
-
height: Math.max(
|
|
103
|
+
width: Math.max(MIN_SHAPE_SIZE2, maxU - minU),
|
|
104
|
+
height: Math.max(MIN_SHAPE_SIZE2, maxV - minV),
|
|
124
105
|
angle,
|
|
125
106
|
minU,
|
|
126
107
|
maxU,
|
|
@@ -164,7 +145,73 @@ function rectangleAngle(sample) {
|
|
|
164
145
|
}
|
|
165
146
|
return Math.atan2(y, x) / 4;
|
|
166
147
|
}
|
|
148
|
+
function pointToSegmentDistance(point, start2, end) {
|
|
149
|
+
const dx = end.x - start2.x;
|
|
150
|
+
const dy = end.y - start2.y;
|
|
151
|
+
const lengthSquared = dx * dx + dy * dy;
|
|
152
|
+
if (lengthSquared === 0) return distance(point, start2);
|
|
153
|
+
const amount = Math.max(0, Math.min(
|
|
154
|
+
1,
|
|
155
|
+
((point.x - start2.x) * dx + (point.y - start2.y) * dy) / lengthSquared
|
|
156
|
+
));
|
|
157
|
+
return Math.hypot(point.x - (start2.x + dx * amount), point.y - (start2.y + dy * amount));
|
|
158
|
+
}
|
|
159
|
+
function polygonError(sample, vertices, scale) {
|
|
160
|
+
let total = 0;
|
|
161
|
+
for (const point of sample) {
|
|
162
|
+
let nearest = Infinity;
|
|
163
|
+
for (let index = 0; index < vertices.length; index += 1) {
|
|
164
|
+
nearest = Math.min(nearest, pointToSegmentDistance(
|
|
165
|
+
point,
|
|
166
|
+
vertices[index],
|
|
167
|
+
vertices[(index + 1) % vertices.length]
|
|
168
|
+
));
|
|
169
|
+
}
|
|
170
|
+
total += nearest;
|
|
171
|
+
}
|
|
172
|
+
return total / sample.length / Math.max(scale, MIN_SHAPE_SIZE2);
|
|
173
|
+
}
|
|
174
|
+
function polygonVertices(bounds, kind) {
|
|
175
|
+
const cos = Math.cos(bounds.angle);
|
|
176
|
+
const sin = Math.sin(bounds.angle);
|
|
177
|
+
const toWorld = (u, v) => ({
|
|
178
|
+
x: u * cos - v * sin,
|
|
179
|
+
y: u * sin + v * cos
|
|
180
|
+
});
|
|
181
|
+
const centerU = (bounds.minU + bounds.maxU) / 2;
|
|
182
|
+
const centerV = (bounds.minV + bounds.maxV) / 2;
|
|
183
|
+
const local = kind === "triangle" ? [[centerU, bounds.minV], [bounds.maxU, bounds.maxV], [bounds.minU, bounds.maxV]] : kind === "diamond" ? [[centerU, bounds.minV], [bounds.maxU, centerV], [centerU, bounds.maxV], [bounds.minU, centerV]] : [[bounds.minU, bounds.minV], [bounds.maxU, bounds.minV], [bounds.maxU, bounds.maxV], [bounds.minU, bounds.maxV]];
|
|
184
|
+
return local.map(([u, v]) => toWorld(u, v));
|
|
185
|
+
}
|
|
186
|
+
function trianglePrediction(sample, pathLength) {
|
|
187
|
+
const baseAngle = principalAngle(sample);
|
|
188
|
+
let best = null;
|
|
189
|
+
for (let turn = 0; turn < 4; turn += 1) {
|
|
190
|
+
const bounds = orientedBounds(sample, baseAngle + turn * Math.PI / 2);
|
|
191
|
+
const vertices = polygonVertices(bounds, "triangle");
|
|
192
|
+
const error = polygonError(sample, vertices, Math.hypot(bounds.width, bounds.height));
|
|
193
|
+
if (!best || error < best.error) best = { bounds, vertices, error };
|
|
194
|
+
}
|
|
195
|
+
if (!best || best.error > 0.045) return null;
|
|
196
|
+
return {
|
|
197
|
+
type: "triangle",
|
|
198
|
+
...best.bounds,
|
|
199
|
+
vertices: best.vertices,
|
|
200
|
+
pathLength
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
function diamondPrediction(sample, pathLength) {
|
|
204
|
+
const bounds = orientedBounds(sample, principalAngle(sample));
|
|
205
|
+
const vertices = polygonVertices(bounds, "diamond");
|
|
206
|
+
const error = polygonError(sample, vertices, Math.hypot(bounds.width, bounds.height));
|
|
207
|
+
if (error > 0.035) return null;
|
|
208
|
+
return { type: "diamond", ...bounds, vertices, pathLength };
|
|
209
|
+
}
|
|
167
210
|
function closedShapePrediction(sample, pathLength) {
|
|
211
|
+
const triangle = trianglePrediction(sample, pathLength);
|
|
212
|
+
if (triangle) return triangle;
|
|
213
|
+
const fittedDiamond = diamondPrediction(sample, pathLength);
|
|
214
|
+
if (fittedDiamond) return fittedDiamond;
|
|
168
215
|
const rectBounds = orientedBounds(sample, rectangleAngle(sample));
|
|
169
216
|
const ellipseBounds = orientedBounds(sample, principalAngle(sample));
|
|
170
217
|
let rectangleError = 0;
|
|
@@ -197,14 +244,29 @@ function closedShapePrediction(sample, pathLength) {
|
|
|
197
244
|
return { type: "freehand", points: sample.map((point) => ({ ...point })), pathLength };
|
|
198
245
|
}
|
|
199
246
|
const bounds = rectangle ? rectBounds : ellipseBounds;
|
|
247
|
+
const normalizedAngle = Math.abs(Math.atan2(Math.sin(rectBounds.angle * 2), Math.cos(rectBounds.angle * 2)) / 2);
|
|
248
|
+
const diamond = rectangle && normalizedAngle >= Math.PI / 7;
|
|
200
249
|
return {
|
|
201
|
-
type: rectangle ? "rectangle" : "circle",
|
|
250
|
+
type: diamond ? "diamond" : rectangle ? "rectangle" : "circle",
|
|
202
251
|
...bounds,
|
|
252
|
+
...diamond ? { vertices: polygonVertices(rectBounds, "rectangle") } : {},
|
|
203
253
|
pathLength
|
|
204
254
|
};
|
|
205
255
|
}
|
|
206
|
-
function
|
|
207
|
-
if (!Array.isArray(sample) || sample.length
|
|
256
|
+
function capGestureSample(sample, maxPoints = MAX_GESTURE_POINTS) {
|
|
257
|
+
if (!Array.isArray(sample) || sample.length <= maxPoints) return Array.isArray(sample) ? sample : [];
|
|
258
|
+
if (maxPoints <= 1) return sample.slice(0, Math.max(0, maxPoints));
|
|
259
|
+
const capped = [sample[0]];
|
|
260
|
+
const step = (sample.length - 1) / (maxPoints - 1);
|
|
261
|
+
for (let index = 1; index < maxPoints - 1; index += 1) {
|
|
262
|
+
capped.push(sample[Math.round(index * step)]);
|
|
263
|
+
}
|
|
264
|
+
capped.push(sample[sample.length - 1]);
|
|
265
|
+
return capped;
|
|
266
|
+
}
|
|
267
|
+
function predictDrawnShape(input) {
|
|
268
|
+
const sample = capGestureSample(input);
|
|
269
|
+
if (sample.length < 2) return null;
|
|
208
270
|
const start2 = sample[0];
|
|
209
271
|
let pathLength = 0;
|
|
210
272
|
let farthestIndex = 1;
|
|
@@ -225,9 +287,9 @@ function predictDrawnShape(sample) {
|
|
|
225
287
|
minY = Math.min(minY, sample[index].y);
|
|
226
288
|
maxY = Math.max(maxY, sample[index].y);
|
|
227
289
|
}
|
|
228
|
-
const diagonal = Math.max(
|
|
290
|
+
const diagonal = Math.max(MIN_SHAPE_SIZE2, Math.hypot(maxX - minX, maxY - minY));
|
|
229
291
|
const end = sample[sample.length - 1];
|
|
230
|
-
const closed = sample.length >= 8 && distance(start2, end) <= diagonal * 0.28 && pathLength >= diagonal *
|
|
292
|
+
const closed = sample.length >= 8 && distance(start2, end) <= diagonal * 0.28 && pathLength >= diagonal * 1.85;
|
|
231
293
|
if (closed) return closedShapePrediction(sample, pathLength);
|
|
232
294
|
const tip = sample[farthestIndex];
|
|
233
295
|
let afterTipLength = 0;
|
|
@@ -248,11 +310,32 @@ function predictDrawnShape(sample) {
|
|
|
248
310
|
type: arrow ? "arrow" : "line",
|
|
249
311
|
start: { ...start2 },
|
|
250
312
|
end: arrow ? { ...tip } : { ...end },
|
|
251
|
-
width: Math.max(
|
|
252
|
-
height: Math.max(
|
|
313
|
+
width: Math.max(MIN_SHAPE_SIZE2, maxX - minX),
|
|
314
|
+
height: Math.max(MIN_SHAPE_SIZE2, maxY - minY),
|
|
253
315
|
pathLength
|
|
254
316
|
};
|
|
255
317
|
}
|
|
318
|
+
|
|
319
|
+
// src/tools/shapeRecognitionTool.js
|
|
320
|
+
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
321
|
+
var MIN_SCREEN_SAMPLE_PX = 4;
|
|
322
|
+
var drawing = false;
|
|
323
|
+
var points = [];
|
|
324
|
+
var previewPath = null;
|
|
325
|
+
var previewFrame = 0;
|
|
326
|
+
var latestPrediction = null;
|
|
327
|
+
function getThemeStroke() {
|
|
328
|
+
return document.body?.classList.contains("theme-dark") ? "#f7f4ff" : "#211a33";
|
|
329
|
+
}
|
|
330
|
+
function getSVGPoint(event) {
|
|
331
|
+
const viewBox = svg.viewBox.baseVal;
|
|
332
|
+
const rect = svg.getBoundingClientRect();
|
|
333
|
+
return {
|
|
334
|
+
x: viewBox.x + (event.clientX - rect.left) / rect.width * viewBox.width,
|
|
335
|
+
y: viewBox.y + (event.clientY - rect.top) / rect.height * viewBox.height,
|
|
336
|
+
pressure: event.pressure || 0.5
|
|
337
|
+
};
|
|
338
|
+
}
|
|
256
339
|
function rotatedPoint(center, x, y, angle) {
|
|
257
340
|
const cos = Math.cos(angle);
|
|
258
341
|
const sin = Math.sin(angle);
|
|
@@ -279,6 +362,12 @@ function pathForPrediction(prediction) {
|
|
|
279
362
|
const rotation = prediction.angle * 180 / Math.PI;
|
|
280
363
|
return `M ${start3.x} ${start3.y} A ${rx} ${ry} ${rotation} 1 0 ${opposite.x} ${opposite.y} A ${rx} ${ry} ${rotation} 1 0 ${start3.x} ${start3.y}`;
|
|
281
364
|
}
|
|
365
|
+
if (prediction.type === "triangle" || prediction.type === "diamond") {
|
|
366
|
+
return prediction.vertices.reduce(
|
|
367
|
+
(path2, point, index) => `${path2}${index === 0 ? "M" : " L"} ${point.x} ${point.y}`,
|
|
368
|
+
""
|
|
369
|
+
) + " Z";
|
|
370
|
+
}
|
|
282
371
|
if (prediction.type === "freehand") {
|
|
283
372
|
if (!prediction.points.length) return "";
|
|
284
373
|
return prediction.points.reduce((path2, point, index) => `${path2}${index === 0 ? "M" : " L"} ${point.x} ${point.y}`, "");
|
|
@@ -314,7 +403,7 @@ function renderPreview() {
|
|
|
314
403
|
latestPrediction = predictDrawnShape(points);
|
|
315
404
|
ensurePreview();
|
|
316
405
|
previewPath.setAttribute("d", pathForPrediction(latestPrediction));
|
|
317
|
-
previewPath.setAttribute("fill",
|
|
406
|
+
previewPath.setAttribute("fill", ["rectangle", "circle", "triangle", "diamond"].includes(latestPrediction?.type) ? "rgba(148, 148, 158, 0.06)" : "none");
|
|
318
407
|
}
|
|
319
408
|
function schedulePreview() {
|
|
320
409
|
if (!previewFrame) previewFrame = requestAnimationFrame(renderPreview);
|
|
@@ -371,6 +460,23 @@ function createPredictedShape(prediction) {
|
|
|
371
460
|
);
|
|
372
461
|
shape.rotation = prediction.angle * 180 / Math.PI;
|
|
373
462
|
shape.draw();
|
|
463
|
+
} else if (prediction.type === "triangle" || prediction.type === "diamond") {
|
|
464
|
+
const polygonPoints = [...prediction.vertices, prediction.vertices[0]];
|
|
465
|
+
shape = new window.FreehandStroke(
|
|
466
|
+
polygonPoints.map((point) => [point.x, point.y, 0.5]),
|
|
467
|
+
{
|
|
468
|
+
stroke: "transparent",
|
|
469
|
+
fill: "transparent",
|
|
470
|
+
fillStyle: "none",
|
|
471
|
+
outlineStroke: style.stroke,
|
|
472
|
+
outlineWidth: style.strokeWidth,
|
|
473
|
+
closedFill: true,
|
|
474
|
+
strokeWidth: style.strokeWidth,
|
|
475
|
+
strokeStyle: style.outline,
|
|
476
|
+
roughness: 0
|
|
477
|
+
}
|
|
478
|
+
);
|
|
479
|
+
shape.recognizedShapeType = prediction.type;
|
|
374
480
|
} else if (prediction.type === "freehand") {
|
|
375
481
|
const brush = window.freehandToolSettings || {};
|
|
376
482
|
shape = new window.FreehandStroke(
|
|
@@ -419,7 +525,7 @@ function handleShapeRecognitionMove(event) {
|
|
|
419
525
|
const last = points[points.length - 1];
|
|
420
526
|
const threshold = MIN_SCREEN_SAMPLE_PX / (window.currentZoom || 1);
|
|
421
527
|
if (distance(last, point) < threshold) return;
|
|
422
|
-
if (points.length <
|
|
528
|
+
if (points.length < MAX_GESTURE_POINTS) {
|
|
423
529
|
points.push(point);
|
|
424
530
|
} else {
|
|
425
531
|
points = [points[0], ...points.slice(2).filter((_, index) => index % 2 === 0), point];
|
|
@@ -460,7 +566,7 @@ function cancelShapeRecognition() {
|
|
|
460
566
|
window.__cancelShapeRecognition = cancelShapeRecognition;
|
|
461
567
|
|
|
462
568
|
// src/tools/paintBucketTool.js
|
|
463
|
-
var
|
|
569
|
+
var OPTION_FILL_SHAPES = /* @__PURE__ */ new Set(["rectangle", "circle"]);
|
|
464
570
|
window.paintBucketSettings = window.paintBucketSettings || {
|
|
465
571
|
fillColor: "#A98DEB",
|
|
466
572
|
fillStyle: "solid"
|
|
@@ -473,37 +579,84 @@ function getSVGPoint2(event) {
|
|
|
473
579
|
y: viewBox.y + (event.clientY - rect.top) / rect.height * viewBox.height
|
|
474
580
|
};
|
|
475
581
|
}
|
|
476
|
-
function
|
|
582
|
+
function isClosedFreehand(shape) {
|
|
583
|
+
if (shape?.shapeName !== "freehandStroke" || !Array.isArray(shape.points) || shape.points.length < 3) return false;
|
|
584
|
+
if (shape.options?.closedFill) return true;
|
|
585
|
+
const first = shape.points[0];
|
|
586
|
+
const last = shape.points[shape.points.length - 1];
|
|
587
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
588
|
+
for (const point of shape.points) {
|
|
589
|
+
const x = Number(point[0]) || 0;
|
|
590
|
+
const y = Number(point[1]) || 0;
|
|
591
|
+
minX = Math.min(minX, x);
|
|
592
|
+
minY = Math.min(minY, y);
|
|
593
|
+
maxX = Math.max(maxX, x);
|
|
594
|
+
maxY = Math.max(maxY, y);
|
|
595
|
+
}
|
|
596
|
+
const diagonal = Math.hypot(maxX - minX, maxY - minY);
|
|
597
|
+
const closeDistance = Math.hypot(last[0] - first[0], last[1] - first[1]);
|
|
598
|
+
const tolerance = Math.max((shape.options?.strokeWidth || 2) * 2, Math.min(16, diagonal * 0.08));
|
|
599
|
+
return diagonal > 0 && closeDistance <= tolerance;
|
|
600
|
+
}
|
|
601
|
+
function isPaintBucketFillable(shape) {
|
|
602
|
+
return OPTION_FILL_SHAPES.has(shape?.shapeName) || shape?.shapeName === "frame" || isClosedFreehand(shape);
|
|
603
|
+
}
|
|
604
|
+
function findShapeAt(x, y) {
|
|
477
605
|
for (let index = shapes.length - 1; index >= 0; index -= 1) {
|
|
478
606
|
const shape = shapes[index];
|
|
479
|
-
if (
|
|
607
|
+
if (typeof shape?.contains !== "function") continue;
|
|
480
608
|
if (shape.contains(x, y)) return shape;
|
|
481
609
|
}
|
|
482
610
|
return null;
|
|
483
611
|
}
|
|
484
|
-
function
|
|
485
|
-
if (!
|
|
486
|
-
const { x, y } = getSVGPoint2(event);
|
|
487
|
-
const shape = findClosedShapeAt(x, y);
|
|
488
|
-
if (!shape) {
|
|
489
|
-
window.dispatchEvent(new CustomEvent("lixsketch:bucket-miss"));
|
|
490
|
-
return;
|
|
491
|
-
}
|
|
492
|
-
const settings = window.paintBucketSettings;
|
|
612
|
+
function fillShape(shape, settings = window.paintBucketSettings) {
|
|
613
|
+
if (!isPaintBucketFillable(shape)) return false;
|
|
493
614
|
const transparent = settings.fillStyle === "none" || settings.fillStyle === "transparent";
|
|
494
615
|
const nextFill = transparent ? "transparent" : settings.fillColor;
|
|
495
616
|
const nextStyle = transparent ? "none" : settings.fillStyle;
|
|
496
|
-
if (shape.
|
|
617
|
+
if (shape.shapeName === "frame") {
|
|
618
|
+
if (shape.fillColor === nextFill && shape.fillStyle === nextStyle) return false;
|
|
619
|
+
const oldOptions2 = { ...shape.options, fillColor: shape.fillColor, fillStyle: shape.fillStyle };
|
|
620
|
+
const newOptions2 = { ...shape.options, fillColor: nextFill, fillStyle: nextStyle };
|
|
621
|
+
pushOptionsChangeAction(shape, oldOptions2, newOptions2);
|
|
622
|
+
shape.fillColor = nextFill;
|
|
623
|
+
shape.fillStyle = nextStyle;
|
|
624
|
+
shape.options = { ...shape.options, fillColor: nextFill, fillStyle: nextStyle };
|
|
625
|
+
shape.draw();
|
|
626
|
+
return true;
|
|
627
|
+
}
|
|
628
|
+
const freehandOptions = shape.shapeName === "freehandStroke" ? {
|
|
629
|
+
closedFill: true,
|
|
630
|
+
outlineStroke: shape.options.outlineStroke || shape.options.stroke
|
|
631
|
+
} : {};
|
|
632
|
+
if (shape.options.fill === nextFill && shape.options.fillStyle === nextStyle && Object.entries(freehandOptions).every(([key, value]) => shape.options[key] === value)) return false;
|
|
497
633
|
const oldOptions = { ...shape.options };
|
|
498
|
-
const newOptions = { ...shape.options, fill: nextFill, fillStyle: nextStyle };
|
|
634
|
+
const newOptions = { ...shape.options, ...freehandOptions, fill: nextFill, fillStyle: nextStyle };
|
|
499
635
|
pushOptionsChangeAction(shape, oldOptions, newOptions);
|
|
500
636
|
shape.options = newOptions;
|
|
501
637
|
shape.draw();
|
|
638
|
+
return true;
|
|
639
|
+
}
|
|
640
|
+
function handlePaintBucketDown(event) {
|
|
641
|
+
if (!window.isPaintBucketToolActive || event.button !== 0) return;
|
|
642
|
+
const { x, y } = getSVGPoint2(event);
|
|
643
|
+
const shape = findShapeAt(x, y);
|
|
644
|
+
if (!shape) {
|
|
645
|
+
window.dispatchEvent(new CustomEvent("lixsketch:bucket-miss"));
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
if (!isPaintBucketFillable(shape)) {
|
|
649
|
+
window.dispatchEvent(new CustomEvent("lixsketch:bucket-not-fillable", {
|
|
650
|
+
detail: { shapeID: shape.shapeID, shapeType: shape.shapeName }
|
|
651
|
+
}));
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
if (!fillShape(shape)) return;
|
|
502
655
|
window.dispatchEvent(new CustomEvent("lixsketch:bucket-filled", { detail: { shapeID: shape.shapeID } }));
|
|
503
656
|
}
|
|
504
657
|
|
|
505
658
|
// src/tools/lassoTool.js
|
|
506
|
-
var
|
|
659
|
+
var MAX_POINTS = 192;
|
|
507
660
|
var MIN_SAMPLE_PX = 4;
|
|
508
661
|
var points2 = [];
|
|
509
662
|
var preview = null;
|
|
@@ -575,7 +728,7 @@ function handleLassoMove(e) {
|
|
|
575
728
|
const p = coords(e), last = points2[points2.length - 1];
|
|
576
729
|
const min = MIN_SAMPLE_PX / (window.currentZoom || 1);
|
|
577
730
|
if (Math.hypot(p.x - last.x, p.y - last.y) < min) return;
|
|
578
|
-
if (points2.length <
|
|
731
|
+
if (points2.length < MAX_POINTS) points2.push(p);
|
|
579
732
|
else points2[points2.length - 1] = p;
|
|
580
733
|
preview?.setAttribute("d", `M ${points2.map((v) => `${v.x} ${v.y}`).join(" L ")} Z`);
|
|
581
734
|
}
|
|
@@ -1094,4 +1247,4 @@ export {
|
|
|
1094
1247
|
handleMainMouseUp,
|
|
1095
1248
|
initEventDispatcher
|
|
1096
1249
|
};
|
|
1097
|
-
//# sourceMappingURL=EventDispatcher-
|
|
1250
|
+
//# sourceMappingURL=EventDispatcher-M5B4EWHJ.js.map
|