@elixpo/lixsketch 4.5.8
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/LICENSE +21 -0
- package/README.md +169 -0
- package/fonts/fonts.css +29 -0
- package/fonts/lixCode.ttf +0 -0
- package/fonts/lixDefault.ttf +0 -0
- package/fonts/lixDocs.ttf +0 -0
- package/fonts/lixFancy.ttf +0 -0
- package/fonts/lixFont.woff2 +0 -0
- package/package.json +49 -0
- package/src/SketchEngine.js +473 -0
- package/src/core/AIRenderer.js +1390 -0
- package/src/core/CopyPaste.js +655 -0
- package/src/core/EraserTrail.js +234 -0
- package/src/core/EventDispatcher.js +371 -0
- package/src/core/GraphEngine.js +150 -0
- package/src/core/GraphMathParser.js +231 -0
- package/src/core/GraphRenderer.js +255 -0
- package/src/core/LayerOrder.js +91 -0
- package/src/core/LixScriptParser.js +1299 -0
- package/src/core/MermaidFlowchartRenderer.js +475 -0
- package/src/core/MermaidSequenceParser.js +197 -0
- package/src/core/MermaidSequenceRenderer.js +479 -0
- package/src/core/ResizeCode.js +175 -0
- package/src/core/ResizeShapes.js +318 -0
- package/src/core/SceneSerializer.js +778 -0
- package/src/core/Selection.js +1861 -0
- package/src/core/SnapGuides.js +273 -0
- package/src/core/UndoRedo.js +1358 -0
- package/src/core/ZoomPan.js +258 -0
- package/src/core/ai-system-prompt.js +663 -0
- package/src/index.js +69 -0
- package/src/shapes/Arrow.js +1979 -0
- package/src/shapes/Circle.js +751 -0
- package/src/shapes/CodeShape.js +244 -0
- package/src/shapes/Frame.js +1460 -0
- package/src/shapes/FreehandStroke.js +724 -0
- package/src/shapes/IconShape.js +265 -0
- package/src/shapes/ImageShape.js +270 -0
- package/src/shapes/Line.js +738 -0
- package/src/shapes/Rectangle.js +794 -0
- package/src/shapes/TextShape.js +225 -0
- package/src/tools/arrowTool.js +581 -0
- package/src/tools/circleTool.js +619 -0
- package/src/tools/codeTool.js +2103 -0
- package/src/tools/eraserTool.js +131 -0
- package/src/tools/frameTool.js +241 -0
- package/src/tools/freehandTool.js +620 -0
- package/src/tools/iconTool.js +1344 -0
- package/src/tools/imageTool.js +1323 -0
- package/src/tools/laserTool.js +317 -0
- package/src/tools/lineTool.js +502 -0
- package/src/tools/rectangleTool.js +544 -0
- package/src/tools/textTool.js +1823 -0
- package/src/utils/imageCompressor.js +107 -0
|
@@ -0,0 +1,619 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
// Circle tool event handlers - extracted from drawCircle.js
|
|
3
|
+
import { pushCreateAction, pushDeleteAction, pushOptionsChangeAction, pushTransformAction, pushFrameAttachmentAction } from '../core/UndoRedo.js';
|
|
4
|
+
import { cleanupAttachments } from './arrowTool.js';
|
|
5
|
+
import { calculateSnap, clearSnapGuides } from '../core/SnapGuides.js';
|
|
6
|
+
|
|
7
|
+
let isDrawingCircle = false;
|
|
8
|
+
let isDraggingShapeCircle = false;
|
|
9
|
+
let isResizingShapeCircle = false;
|
|
10
|
+
let isRotatingShapeCircle = false;
|
|
11
|
+
let resizingAnchorIndexCircle = null;
|
|
12
|
+
|
|
13
|
+
let startRotationMouseAngleCircle = null;
|
|
14
|
+
let startShapeRotationCircle = null;
|
|
15
|
+
const rc = rough.svg(svg);
|
|
16
|
+
let startX, startY;
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
let circleStrokecolor = "#fff";
|
|
20
|
+
let circleBackgroundColor = "transparent";
|
|
21
|
+
let circleFillStyleValue = "none";
|
|
22
|
+
let circleStrokeThicknes = 2;
|
|
23
|
+
let circleOutlineStyle = "solid";
|
|
24
|
+
|
|
25
|
+
let dragOldPosCircle = null;
|
|
26
|
+
let draggedShapeInitialFrameCircle = null;
|
|
27
|
+
let hoveredFrameCircle = null;
|
|
28
|
+
let colorOptionsCircle = document.querySelectorAll(".circleStrokeSpan");
|
|
29
|
+
let backgroundColorOptionsCircle = document.querySelectorAll(".circleBackgroundSpan");
|
|
30
|
+
let fillStyleOptionsCircle = document.querySelectorAll(".circleFillStyleSpan");
|
|
31
|
+
let strokeThicknessValueCircle = document.querySelectorAll(".circleStrokeThickSpan");
|
|
32
|
+
let outlineStyleValueCircle = document.querySelectorAll(".circleOutlineStyle");
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
function getSVGCoordsFromMouse(e) {
|
|
37
|
+
const viewBox = svg.viewBox.baseVal;
|
|
38
|
+
const rect = svg.getBoundingClientRect();
|
|
39
|
+
const mouseX = e.clientX - rect.left;
|
|
40
|
+
const mouseY = e.clientY - rect.top;
|
|
41
|
+
const svgX = viewBox.x + (mouseX / rect.width) * viewBox.width;
|
|
42
|
+
const svgY = viewBox.y + (mouseY / rect.height) * viewBox.height;
|
|
43
|
+
return { x: svgX, y: svgY };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
function deleteCurrentShape() {
|
|
48
|
+
if (currentShape && currentShape.shapeName === 'circle') {
|
|
49
|
+
const idx = shapes.indexOf(currentShape);
|
|
50
|
+
if (idx !== -1) shapes.splice(idx, 1);
|
|
51
|
+
|
|
52
|
+
// Clean up any arrow attachments before deleting
|
|
53
|
+
cleanupAttachments(currentShape);
|
|
54
|
+
|
|
55
|
+
if (currentShape.group.parentNode) {
|
|
56
|
+
currentShape.group.parentNode.removeChild(currentShape.group);
|
|
57
|
+
}
|
|
58
|
+
pushDeleteAction(currentShape);
|
|
59
|
+
currentShape = null;
|
|
60
|
+
disableAllSideBars();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
document.addEventListener('keydown', (e) => {
|
|
65
|
+
if (e.key === 'Delete' && currentShape && currentShape.shapeName === 'circle') {
|
|
66
|
+
deleteCurrentShape();
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const handleMouseDown = (e) => {
|
|
71
|
+
const {x: svgMouseX, y: svgMouseY} = getSVGCoordsFromMouse(e);
|
|
72
|
+
|
|
73
|
+
if(isCircleToolActive)
|
|
74
|
+
{
|
|
75
|
+
startX = svgMouseX;
|
|
76
|
+
startY = svgMouseY;
|
|
77
|
+
isDrawingCircle = true;
|
|
78
|
+
|
|
79
|
+
if(currentShape)
|
|
80
|
+
{
|
|
81
|
+
currentShape.removeSelection();
|
|
82
|
+
currentShape = null;
|
|
83
|
+
disableAllSideBars();
|
|
84
|
+
}
|
|
85
|
+
let initialOptions = {
|
|
86
|
+
stroke: circleStrokecolor,
|
|
87
|
+
fill: circleBackgroundColor,
|
|
88
|
+
fillStyle: circleFillStyleValue,
|
|
89
|
+
strokeWidth: circleStrokeThicknes,
|
|
90
|
+
};
|
|
91
|
+
if(circleOutlineStyle === "dashed") {
|
|
92
|
+
initialOptions.strokeDasharray = "5,5";
|
|
93
|
+
}
|
|
94
|
+
else if(circleOutlineStyle === "dotted") {
|
|
95
|
+
initialOptions.strokeDasharray = "2,8";
|
|
96
|
+
} else {
|
|
97
|
+
initialOptions.strokeDasharray = "";
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
currentShape = new Circle(startX, startY, 0, 0, initialOptions);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
else if(isSelectionToolActive)
|
|
104
|
+
{
|
|
105
|
+
let clickedOnShape = false;
|
|
106
|
+
if (currentShape && currentShape.shapeName === 'circle' && currentShape.isSelected)
|
|
107
|
+
{
|
|
108
|
+
const anchorInfo = currentShape.isNearAnchor(svgMouseX, svgMouseY);
|
|
109
|
+
if (anchorInfo) {
|
|
110
|
+
dragOldPosCircle = {
|
|
111
|
+
x: currentShape.x,
|
|
112
|
+
y: currentShape.y,
|
|
113
|
+
rx: currentShape.rx,
|
|
114
|
+
ry: currentShape.ry,
|
|
115
|
+
rotation: currentShape.rotation
|
|
116
|
+
};
|
|
117
|
+
if(anchorInfo.type === 'resize')
|
|
118
|
+
{
|
|
119
|
+
isResizingShapeCircle = true;
|
|
120
|
+
resizingAnchorIndexCircle = anchorInfo.index;
|
|
121
|
+
}
|
|
122
|
+
else if(anchorInfo.type === 'rotate')
|
|
123
|
+
{
|
|
124
|
+
isRotatingShapeCircle = true;
|
|
125
|
+
const CTM = currentShape.group.getCTM();
|
|
126
|
+
if(CTM)
|
|
127
|
+
{
|
|
128
|
+
const svgPoint = svg.createSVGPoint();
|
|
129
|
+
svgPoint.x = currentShape.x;
|
|
130
|
+
svgPoint.y = currentShape.y;
|
|
131
|
+
const centerSVGPoint = svgPoint.matrixTransform(CTM);
|
|
132
|
+
startRotationMouseAngleCircle = Math.atan2(svgMouseY - centerSVGPoint.y, svgMouseX - centerSVGPoint.x) * (180 / Math.PI);
|
|
133
|
+
startShapeRotationCircle = currentShape.rotation;
|
|
134
|
+
}
|
|
135
|
+
else
|
|
136
|
+
{
|
|
137
|
+
isRotatingShapeCircle = false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
clickedOnShape = true;
|
|
141
|
+
}
|
|
142
|
+
else if (currentShape.contains(svgMouseX, svgMouseY))
|
|
143
|
+
{
|
|
144
|
+
isDraggingShapeCircle = true;
|
|
145
|
+
dragOldPosCircle = {
|
|
146
|
+
x: currentShape.x,
|
|
147
|
+
y: currentShape.y,
|
|
148
|
+
rx: currentShape.rx,
|
|
149
|
+
ry: currentShape.ry,
|
|
150
|
+
rotation: currentShape.rotation
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// Store initial frame state
|
|
154
|
+
draggedShapeInitialFrameCircle = currentShape.parentFrame || null;
|
|
155
|
+
|
|
156
|
+
// Temporarily remove from frame clipping if dragging
|
|
157
|
+
if (currentShape.parentFrame) {
|
|
158
|
+
currentShape.parentFrame.temporarilyRemoveFromFrame(currentShape);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
startX = svgMouseX;
|
|
162
|
+
startY = svgMouseY;
|
|
163
|
+
clickedOnShape = true;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (!clickedOnShape)
|
|
167
|
+
{
|
|
168
|
+
let shapeToSelect = null;
|
|
169
|
+
for (let i = shapes.length - 1; i >= 0; i--) {
|
|
170
|
+
const shape = shapes[i];
|
|
171
|
+
if (shape.shapeName === 'circle' && shape.contains(svgMouseX, svgMouseY)) {
|
|
172
|
+
shapeToSelect = shape;
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (currentShape && currentShape !== shapeToSelect) {
|
|
177
|
+
currentShape.removeSelection();
|
|
178
|
+
currentShape = null;
|
|
179
|
+
disableAllSideBars();
|
|
180
|
+
}
|
|
181
|
+
if(shapeToSelect)
|
|
182
|
+
{
|
|
183
|
+
currentShape = shapeToSelect;
|
|
184
|
+
currentShape.isSelected = true;
|
|
185
|
+
currentShape.draw();
|
|
186
|
+
isDraggingShapeCircle = true;
|
|
187
|
+
dragOldPosCircle = {
|
|
188
|
+
x: currentShape.x,
|
|
189
|
+
y: currentShape.y,
|
|
190
|
+
rx: currentShape.rx,
|
|
191
|
+
ry: currentShape.ry,
|
|
192
|
+
rotation: currentShape.rotation
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
// Store initial frame state
|
|
196
|
+
draggedShapeInitialFrameCircle = currentShape.parentFrame || null;
|
|
197
|
+
|
|
198
|
+
// Temporarily remove from frame clipping if dragging
|
|
199
|
+
if (currentShape.parentFrame) {
|
|
200
|
+
currentShape.parentFrame.temporarilyRemoveFromFrame(currentShape);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
startX = svgMouseX;
|
|
204
|
+
startY = svgMouseY;
|
|
205
|
+
clickedOnShape = true;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if(!clickedOnShape && currentShape) {
|
|
209
|
+
currentShape.removeSelection();
|
|
210
|
+
currentShape = null;
|
|
211
|
+
disableAllSideBars();
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const handleMouseMove = (e) => {
|
|
217
|
+
|
|
218
|
+
const {x: svgMouseX, y: svgMouseY} = getSVGCoordsFromMouse(e);
|
|
219
|
+
|
|
220
|
+
// Keep lastMousePos in screen coordinates for other functions
|
|
221
|
+
const svgRect = svg.getBoundingClientRect();
|
|
222
|
+
lastMousePos = {
|
|
223
|
+
x: e.clientX - svgRect.left,
|
|
224
|
+
y: e.clientY - svgRect.top
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
if(isDrawingCircle && isCircleToolActive && currentShape)
|
|
228
|
+
{
|
|
229
|
+
currentShape.x = (startX + svgMouseX) / 2;
|
|
230
|
+
currentShape.y = (startY + svgMouseY) / 2;
|
|
231
|
+
currentShape.rx = Math.abs(svgMouseX - startX) / 2;
|
|
232
|
+
currentShape.ry = Math.abs(svgMouseY - startY) / 2;
|
|
233
|
+
currentShape.draw();
|
|
234
|
+
|
|
235
|
+
// Check for frame containment while drawing (but don't apply clipping yet)
|
|
236
|
+
shapes.forEach(frame => {
|
|
237
|
+
if (frame.shapeName === 'frame') {
|
|
238
|
+
if (frame.isShapeInFrame(currentShape)) {
|
|
239
|
+
frame.highlightFrame();
|
|
240
|
+
hoveredFrameCircle = frame;
|
|
241
|
+
} else if (hoveredFrameCircle === frame) {
|
|
242
|
+
frame.removeHighlight();
|
|
243
|
+
hoveredFrameCircle = null;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
else if (isDraggingShapeCircle && currentShape && currentShape.isSelected) {
|
|
249
|
+
const dx = svgMouseX - startX;
|
|
250
|
+
const dy = svgMouseY - startY;
|
|
251
|
+
currentShape.move(dx, dy);
|
|
252
|
+
startX = svgMouseX;
|
|
253
|
+
startY = svgMouseY;
|
|
254
|
+
|
|
255
|
+
// Snap guides
|
|
256
|
+
if (window.__sketchStoreApi && window.__sketchStoreApi.getState().snapToObjects) {
|
|
257
|
+
const snap = calculateSnap(currentShape, e.shiftKey, e.clientX, e.clientY);
|
|
258
|
+
if (snap.dx || snap.dy) {
|
|
259
|
+
currentShape.move(snap.dx, snap.dy);
|
|
260
|
+
}
|
|
261
|
+
} else {
|
|
262
|
+
clearSnapGuides();
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
else if(isResizingShapeCircle && currentShape && currentShape.isSelected)
|
|
267
|
+
{
|
|
268
|
+
currentShape.updatePosition(resizingAnchorIndexCircle, svgMouseX, svgMouseY);
|
|
269
|
+
currentShape._skipAnchors = true;
|
|
270
|
+
currentShape.draw();
|
|
271
|
+
currentShape._skipAnchors = false;
|
|
272
|
+
}
|
|
273
|
+
else if (isRotatingShapeCircle && currentShape && currentShape.isSelected)
|
|
274
|
+
{
|
|
275
|
+
const CTM = currentShape.group.getCTM();
|
|
276
|
+
if(CTM) {
|
|
277
|
+
const svgPoint = svg.createSVGPoint();
|
|
278
|
+
svgPoint.x = currentShape.x;
|
|
279
|
+
svgPoint.y = currentShape.y;
|
|
280
|
+
const centerSVGPoint = svgPoint.matrixTransform(CTM);
|
|
281
|
+
const currentMouseAngle = Math.atan2(svgMouseY - centerSVGPoint.y, svgMouseX - centerSVGPoint.x) * (180 / Math.PI);
|
|
282
|
+
const angleDiff = currentMouseAngle - startRotationMouseAngleCircle;
|
|
283
|
+
let newRotation = startShapeRotationCircle + angleDiff;
|
|
284
|
+
const snapAngle = 15;
|
|
285
|
+
if (e.shiftKey) {
|
|
286
|
+
newRotation = Math.round(newRotation / snapAngle) * snapAngle;
|
|
287
|
+
}
|
|
288
|
+
currentShape.rotate(newRotation);
|
|
289
|
+
currentShape._skipAnchors = true;
|
|
290
|
+
currentShape.draw();
|
|
291
|
+
currentShape._skipAnchors = false;
|
|
292
|
+
svg.style.cursor = 'grabbing';
|
|
293
|
+
}
|
|
294
|
+
else
|
|
295
|
+
{
|
|
296
|
+
isRotatingShapeCircle = false;
|
|
297
|
+
svg.style.cursor = 'default';
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
else if (isSelectionToolActive && !isDrawingCircle && currentShape && currentShape.isSelected)
|
|
301
|
+
{
|
|
302
|
+
const anchorInfo = currentShape.isNearAnchor(svgMouseX, svgMouseY);
|
|
303
|
+
if(anchorInfo)
|
|
304
|
+
{
|
|
305
|
+
if(anchorInfo.type === 'resize') {
|
|
306
|
+
const baseDirection = anchorInfo.index;
|
|
307
|
+
const rotatedCursor = currentShape.getRotatedCursor(baseDirection, currentShape.rotation);
|
|
308
|
+
svg.style.cursor = rotatedCursor + '-resize';
|
|
309
|
+
}
|
|
310
|
+
else if(anchorInfo.type === 'rotate') {
|
|
311
|
+
svg.style.cursor = 'grab';
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
else if(currentShape.contains(svgMouseX, svgMouseY)) {
|
|
315
|
+
svg.style.cursor = 'move';
|
|
316
|
+
}
|
|
317
|
+
else
|
|
318
|
+
{
|
|
319
|
+
svg.style.cursor = 'default';
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
else if (isSelectionToolActive && !isDrawingCircle && !isDraggingShapeCircle && !isResizingShapeCircle && !isRotatingShapeCircle)
|
|
323
|
+
{
|
|
324
|
+
let hoveredShape = null;
|
|
325
|
+
for (let i = shapes.length - 1; i >= 0; i--) {
|
|
326
|
+
const shape = shapes[i];
|
|
327
|
+
if (shape.shapeName === 'circle' && shape.contains(svgMouseX, svgMouseY)) {
|
|
328
|
+
hoveredShape = shape;
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
if (hoveredShape) {
|
|
333
|
+
svg.style.cursor = 'pointer';
|
|
334
|
+
} else {
|
|
335
|
+
svg.style.cursor = 'default';
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const handleMouseUp = (e) => {
|
|
341
|
+
if (isDrawingCircle && currentShape) {
|
|
342
|
+
if(currentShape.rx === 0 && currentShape.ry === 0) {
|
|
343
|
+
if (currentShape.group.parentNode) {
|
|
344
|
+
currentShape.group.parentNode.removeChild(currentShape.group);
|
|
345
|
+
}
|
|
346
|
+
currentShape = null;
|
|
347
|
+
} else {
|
|
348
|
+
shapes.push(currentShape);
|
|
349
|
+
pushCreateAction(currentShape);
|
|
350
|
+
|
|
351
|
+
// Check for frame containment and track attachment
|
|
352
|
+
const finalFrame = hoveredFrameCircle;
|
|
353
|
+
if (finalFrame) {
|
|
354
|
+
finalFrame.addShapeToFrame(currentShape);
|
|
355
|
+
// Track the attachment for undo
|
|
356
|
+
pushFrameAttachmentAction(finalFrame, currentShape, 'attach', null);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Auto-select the drawn shape and switch to selection tool
|
|
360
|
+
const drawnShape = currentShape;
|
|
361
|
+
if (window.__sketchStoreApi) window.__sketchStoreApi.setActiveTool('select', { afterDraw: true });
|
|
362
|
+
currentShape = drawnShape;
|
|
363
|
+
currentShape.isSelected = true;
|
|
364
|
+
if (typeof currentShape.addAnchors === 'function') {
|
|
365
|
+
currentShape.addAnchors();
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Clear frame highlighting
|
|
370
|
+
if (hoveredFrameCircle) {
|
|
371
|
+
hoveredFrameCircle.removeHighlight();
|
|
372
|
+
hoveredFrameCircle = null;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if((isDraggingShapeCircle || isResizingShapeCircle || isRotatingShapeCircle) && dragOldPosCircle && currentShape) {
|
|
377
|
+
const newPos = {
|
|
378
|
+
x: currentShape.x,
|
|
379
|
+
y: currentShape.y,
|
|
380
|
+
rx: currentShape.rx,
|
|
381
|
+
ry: currentShape.ry,
|
|
382
|
+
rotation: currentShape.rotation,
|
|
383
|
+
parentFrame: currentShape.parentFrame
|
|
384
|
+
};
|
|
385
|
+
const oldPos = {
|
|
386
|
+
...dragOldPosCircle,
|
|
387
|
+
parentFrame: draggedShapeInitialFrameCircle
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
const stateChanged = oldPos.x !== newPos.x || oldPos.y !== newPos.y ||
|
|
391
|
+
oldPos.rx !== newPos.rx || oldPos.ry !== newPos.ry ||
|
|
392
|
+
oldPos.rotation !== newPos.rotation;
|
|
393
|
+
|
|
394
|
+
const frameChanged = oldPos.parentFrame !== newPos.parentFrame;
|
|
395
|
+
|
|
396
|
+
if (stateChanged || frameChanged) {
|
|
397
|
+
const oldPosForUndo = {
|
|
398
|
+
x: oldPos.x,
|
|
399
|
+
y: oldPos.y,
|
|
400
|
+
rx: oldPos.rx,
|
|
401
|
+
ry: oldPos.ry,
|
|
402
|
+
rotation: oldPos.rotation,
|
|
403
|
+
parentFrame: oldPos.parentFrame
|
|
404
|
+
};
|
|
405
|
+
const newPosForUndo = {
|
|
406
|
+
x: newPos.x,
|
|
407
|
+
y: newPos.y,
|
|
408
|
+
rx: newPos.rx,
|
|
409
|
+
ry: newPos.ry,
|
|
410
|
+
rotation: newPos.rotation,
|
|
411
|
+
parentFrame: newPos.parentFrame
|
|
412
|
+
};
|
|
413
|
+
pushTransformAction(currentShape, oldPosForUndo, newPosForUndo);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Handle frame containment changes after drag
|
|
417
|
+
if (isDraggingShapeCircle) {
|
|
418
|
+
const finalFrame = hoveredFrameCircle;
|
|
419
|
+
|
|
420
|
+
// If shape moved to a different frame
|
|
421
|
+
if (draggedShapeInitialFrameCircle !== finalFrame) {
|
|
422
|
+
// Remove from initial frame
|
|
423
|
+
if (draggedShapeInitialFrameCircle) {
|
|
424
|
+
draggedShapeInitialFrameCircle.removeShapeFromFrame(currentShape);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Add to new frame
|
|
428
|
+
if (finalFrame) {
|
|
429
|
+
finalFrame.addShapeToFrame(currentShape);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Track the frame change for undo
|
|
433
|
+
if (frameChanged) {
|
|
434
|
+
pushFrameAttachmentAction(finalFrame || draggedShapeInitialFrameCircle, currentShape,
|
|
435
|
+
finalFrame ? 'attach' : 'detach', draggedShapeInitialFrameCircle);
|
|
436
|
+
}
|
|
437
|
+
} else if (draggedShapeInitialFrameCircle) {
|
|
438
|
+
// Shape stayed in same frame, restore clipping
|
|
439
|
+
draggedShapeInitialFrameCircle.restoreToFrame(currentShape);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
dragOldPosCircle = null;
|
|
444
|
+
draggedShapeInitialFrameCircle = null;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Clear frame highlighting
|
|
448
|
+
if (hoveredFrameCircle) {
|
|
449
|
+
hoveredFrameCircle.removeHighlight();
|
|
450
|
+
hoveredFrameCircle = null;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
clearSnapGuides();
|
|
454
|
+
isDrawingCircle = false;
|
|
455
|
+
isDraggingShapeCircle = false;
|
|
456
|
+
isResizingShapeCircle = false;
|
|
457
|
+
isRotatingShapeCircle = false;
|
|
458
|
+
resizingAnchorIndexCircle = null;
|
|
459
|
+
startRotationMouseAngleCircle = null;
|
|
460
|
+
startShapeRotationCircle = 0;
|
|
461
|
+
svg.style.cursor = 'default';
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
colorOptionsCircle.forEach(span => {
|
|
466
|
+
span.addEventListener('click', function(event) {
|
|
467
|
+
event.stopPropagation();
|
|
468
|
+
if (currentShape && currentShape.shapeName === 'circle' && currentShape.isSelected) {
|
|
469
|
+
const color = this.getAttribute('data-id');
|
|
470
|
+
const oldOptions = {...currentShape.options};
|
|
471
|
+
currentShape.options.stroke = color;
|
|
472
|
+
currentShape.draw();
|
|
473
|
+
currentShape.updateSidebar();
|
|
474
|
+
pushOptionsChangeAction(currentShape, oldOptions);
|
|
475
|
+
}
|
|
476
|
+
else
|
|
477
|
+
{
|
|
478
|
+
circleStrokecolor = this.getAttribute('data-id');
|
|
479
|
+
}
|
|
480
|
+
colorOptionsCircle.forEach(span => {
|
|
481
|
+
span.classList.remove('selected');
|
|
482
|
+
});
|
|
483
|
+
this.classList.add('selected');
|
|
484
|
+
});
|
|
485
|
+
});
|
|
486
|
+
backgroundColorOptionsCircle.forEach(span => {
|
|
487
|
+
span.addEventListener('click', function(event) {
|
|
488
|
+
event.stopPropagation();
|
|
489
|
+
if (currentShape && currentShape.shapeName === 'circle' && currentShape.isSelected) {
|
|
490
|
+
const color = this.getAttribute('data-id');
|
|
491
|
+
const oldOptions = {...currentShape.options};
|
|
492
|
+
currentShape.options.fill = color;
|
|
493
|
+
currentShape.draw();
|
|
494
|
+
currentShape.updateSidebar();
|
|
495
|
+
pushOptionsChangeAction(currentShape, oldOptions);
|
|
496
|
+
}
|
|
497
|
+
else
|
|
498
|
+
{
|
|
499
|
+
circleBackgroundColor = this.getAttribute('data-id');
|
|
500
|
+
}
|
|
501
|
+
backgroundColorOptionsCircle.forEach(span => {
|
|
502
|
+
span.classList.remove('selected');
|
|
503
|
+
});
|
|
504
|
+
this.classList.add('selected');
|
|
505
|
+
});
|
|
506
|
+
});
|
|
507
|
+
fillStyleOptionsCircle.forEach(span => {
|
|
508
|
+
span.addEventListener('click', function(event) {
|
|
509
|
+
event.stopPropagation();
|
|
510
|
+
if (currentShape && currentShape.shapeName === 'circle' && currentShape.isSelected) {
|
|
511
|
+
const style = this.getAttribute('data-id');
|
|
512
|
+
const oldOptions = {...currentShape.options};
|
|
513
|
+
currentShape.options.fillStyle = style;
|
|
514
|
+
currentShape.draw();
|
|
515
|
+
currentShape.updateSidebar();
|
|
516
|
+
pushOptionsChangeAction(currentShape, oldOptions);
|
|
517
|
+
}
|
|
518
|
+
else
|
|
519
|
+
{
|
|
520
|
+
circleFillStyleValue = this.getAttribute('data-id');
|
|
521
|
+
}
|
|
522
|
+
fillStyleOptionsCircle.forEach(span => {
|
|
523
|
+
span.classList.remove('selected');
|
|
524
|
+
});
|
|
525
|
+
this.classList.add('selected');
|
|
526
|
+
});
|
|
527
|
+
});
|
|
528
|
+
strokeThicknessValueCircle.forEach(span => {
|
|
529
|
+
span.addEventListener('click', function(event) {
|
|
530
|
+
event.stopPropagation();
|
|
531
|
+
if (currentShape && currentShape.shapeName === 'circle' && currentShape.isSelected) {
|
|
532
|
+
const thick = parseInt(this.getAttribute('data-id'), 10);
|
|
533
|
+
const oldOptions = {...currentShape.options};
|
|
534
|
+
currentShape.options.strokeWidth = thick;
|
|
535
|
+
currentShape.draw();
|
|
536
|
+
currentShape.updateSidebar();
|
|
537
|
+
pushOptionsChangeAction(currentShape, oldOptions);
|
|
538
|
+
}
|
|
539
|
+
else
|
|
540
|
+
{
|
|
541
|
+
circleStrokeThicknes = parseInt(this.getAttribute('data-id'), 10);
|
|
542
|
+
}
|
|
543
|
+
strokeThicknessValueCircle.forEach(span => {
|
|
544
|
+
span.classList.remove('selected');
|
|
545
|
+
});
|
|
546
|
+
this.classList.add('selected');
|
|
547
|
+
});
|
|
548
|
+
});
|
|
549
|
+
outlineStyleValueCircle.forEach(span => {
|
|
550
|
+
span.addEventListener('click', function(event) {
|
|
551
|
+
event.stopPropagation();
|
|
552
|
+
if (currentShape && currentShape.shapeName === 'circle' && currentShape.isSelected) {
|
|
553
|
+
const style = this.getAttribute('data-id');
|
|
554
|
+
const oldOptions = {...currentShape.options};
|
|
555
|
+
if (style === "dashed") {
|
|
556
|
+
currentShape.options.strokeDasharray = "5,5";
|
|
557
|
+
} else if (style === "dotted") {
|
|
558
|
+
currentShape.options.strokeDasharray = "2,8";
|
|
559
|
+
} else {
|
|
560
|
+
currentShape.options.strokeDasharray = "";
|
|
561
|
+
}
|
|
562
|
+
currentShape.draw();
|
|
563
|
+
currentShape.updateSidebar();
|
|
564
|
+
pushOptionsChangeAction(currentShape, oldOptions);
|
|
565
|
+
}
|
|
566
|
+
else
|
|
567
|
+
{
|
|
568
|
+
circleOutlineStyle = this.getAttribute('data-id');
|
|
569
|
+
}
|
|
570
|
+
outlineStyleValueCircle.forEach(span => {
|
|
571
|
+
span.classList.remove('selected');
|
|
572
|
+
});
|
|
573
|
+
this.classList.add('selected');
|
|
574
|
+
});
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
window.Circle = Circle;
|
|
578
|
+
|
|
579
|
+
// Bridge circle tool settings to React sidebar
|
|
580
|
+
window.circleToolSettings = {
|
|
581
|
+
get strokeColor() { return circleStrokecolor; },
|
|
582
|
+
set strokeColor(v) { circleStrokecolor = v; },
|
|
583
|
+
get bgColor() { return circleBackgroundColor; },
|
|
584
|
+
set bgColor(v) { circleBackgroundColor = v; },
|
|
585
|
+
get fillStyle() { return circleFillStyleValue; },
|
|
586
|
+
set fillStyle(v) { circleFillStyleValue = v; },
|
|
587
|
+
get strokeWidth() { return circleStrokeThicknes; },
|
|
588
|
+
set strokeWidth(v) { circleStrokeThicknes = v; },
|
|
589
|
+
get outlineStyle() { return circleOutlineStyle; },
|
|
590
|
+
set outlineStyle(v) { circleOutlineStyle = v; },
|
|
591
|
+
};
|
|
592
|
+
window.updateSelectedCircleStyle = function(changes) {
|
|
593
|
+
if (currentShape && currentShape.shapeName === 'circle' && currentShape.isSelected) {
|
|
594
|
+
pushOptionsChangeAction(currentShape, { ...currentShape.options });
|
|
595
|
+
if (changes.stroke !== undefined) { circleStrokecolor = changes.stroke; currentShape.options.stroke = changes.stroke; }
|
|
596
|
+
if (changes.fill !== undefined) { circleBackgroundColor = changes.fill; currentShape.options.fill = changes.fill; }
|
|
597
|
+
if (changes.fillStyle !== undefined) { circleFillStyleValue = changes.fillStyle; currentShape.options.fillStyle = changes.fillStyle; }
|
|
598
|
+
if (changes.strokeWidth !== undefined) { circleStrokeThicknes = changes.strokeWidth; currentShape.options.strokeWidth = changes.strokeWidth; }
|
|
599
|
+
if (changes.outlineStyle !== undefined) {
|
|
600
|
+
circleOutlineStyle = changes.outlineStyle;
|
|
601
|
+
if (changes.outlineStyle === "dashed") currentShape.options.strokeDasharray = "5,5";
|
|
602
|
+
else if (changes.outlineStyle === "dotted") currentShape.options.strokeDasharray = "2,8";
|
|
603
|
+
else currentShape.options.strokeDasharray = "";
|
|
604
|
+
}
|
|
605
|
+
currentShape.draw();
|
|
606
|
+
} else {
|
|
607
|
+
if (changes.stroke !== undefined) circleStrokecolor = changes.stroke;
|
|
608
|
+
if (changes.fill !== undefined) circleBackgroundColor = changes.fill;
|
|
609
|
+
if (changes.fillStyle !== undefined) circleFillStyleValue = changes.fillStyle;
|
|
610
|
+
if (changes.strokeWidth !== undefined) circleStrokeThicknes = changes.strokeWidth;
|
|
611
|
+
if (changes.outlineStyle !== undefined) circleOutlineStyle = changes.outlineStyle;
|
|
612
|
+
}
|
|
613
|
+
};
|
|
614
|
+
|
|
615
|
+
export {
|
|
616
|
+
handleMouseDown as handleMouseDownCircle,
|
|
617
|
+
handleMouseMove as handleMouseMoveCircle,
|
|
618
|
+
handleMouseUp as handleMouseUpCircle,
|
|
619
|
+
}
|