@teachinglab/omd 0.7.5 → 0.7.7
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/canvas/tools/SelectTool.js +291 -113
- package/package.json +1 -1
|
@@ -41,6 +41,7 @@ export class SelectTool extends Tool {
|
|
|
41
41
|
/** @private - OMD dragging state */
|
|
42
42
|
this.isDraggingOMD = false;
|
|
43
43
|
this.draggedOMDElement = null;
|
|
44
|
+
this.selectedOMDElements = new Set();
|
|
44
45
|
|
|
45
46
|
/** @private - Stroke dragging state */
|
|
46
47
|
this.isDraggingStrokes = false;
|
|
@@ -100,8 +101,11 @@ export class SelectTool extends Tool {
|
|
|
100
101
|
}
|
|
101
102
|
return;
|
|
102
103
|
} else {
|
|
103
|
-
// Clicking on a stroke segment
|
|
104
|
-
|
|
104
|
+
// Clicking on a stroke segment
|
|
105
|
+
if (!event.shiftKey) {
|
|
106
|
+
this.resizeHandleManager.clearSelection();
|
|
107
|
+
this.selectedOMDElements.clear();
|
|
108
|
+
}
|
|
105
109
|
this._handleSegmentClick(segmentSelection, event.shiftKey);
|
|
106
110
|
|
|
107
111
|
// Prepare for drag immediately after selection
|
|
@@ -114,14 +118,38 @@ export class SelectTool extends Tool {
|
|
|
114
118
|
}
|
|
115
119
|
}
|
|
116
120
|
} else if (omdElement) {
|
|
117
|
-
// Clicking on an OMD visual
|
|
121
|
+
// Clicking on an OMD visual
|
|
122
|
+
|
|
123
|
+
// Check if already selected
|
|
124
|
+
if (this.selectedOMDElements.has(omdElement)) {
|
|
125
|
+
// Already selected - prepare for drag
|
|
126
|
+
this.isDraggingOMD = true;
|
|
127
|
+
this.draggedOMDElement = omdElement; // Primary drag target
|
|
128
|
+
this.startPoint = { x: event.x, y: event.y };
|
|
129
|
+
|
|
130
|
+
// Show resize handles if this is the only selected element
|
|
131
|
+
if (this.selectedOMDElements.size === 1) {
|
|
132
|
+
this.resizeHandleManager.selectElement(omdElement);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (this.canvas.eventManager) {
|
|
136
|
+
this.canvas.eventManager.isDrawing = true;
|
|
137
|
+
}
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// New selection
|
|
118
142
|
if (!event.shiftKey) {
|
|
119
143
|
this.selectedSegments.clear();
|
|
120
144
|
this._clearSelectionVisuals();
|
|
145
|
+
this.selectedOMDElements.clear();
|
|
146
|
+
this.resizeHandleManager.clearSelection();
|
|
121
147
|
}
|
|
148
|
+
|
|
149
|
+
this.selectedOMDElements.add(omdElement);
|
|
122
150
|
this.resizeHandleManager.selectElement(omdElement);
|
|
123
151
|
|
|
124
|
-
//
|
|
152
|
+
// Start tracking for potential drag operation
|
|
125
153
|
this.isDraggingOMD = true;
|
|
126
154
|
this.draggedOMDElement = omdElement;
|
|
127
155
|
this.startPoint = { x: event.x, y: event.y };
|
|
@@ -131,11 +159,32 @@ export class SelectTool extends Tool {
|
|
|
131
159
|
this.canvas.eventManager.isDrawing = true;
|
|
132
160
|
}
|
|
133
161
|
|
|
134
|
-
// Don't start box selection - we're either resizing or will be dragging
|
|
135
162
|
return;
|
|
136
163
|
} else {
|
|
164
|
+
// Check if clicking inside existing selection bounds
|
|
165
|
+
const selectionBounds = this._getSelectionBounds();
|
|
166
|
+
if (selectionBounds &&
|
|
167
|
+
event.x >= selectionBounds.x &&
|
|
168
|
+
event.x <= selectionBounds.x + selectionBounds.width &&
|
|
169
|
+
event.y >= selectionBounds.y &&
|
|
170
|
+
event.y <= selectionBounds.y + selectionBounds.height) {
|
|
171
|
+
|
|
172
|
+
// Drag the selection (strokes AND OMD elements)
|
|
173
|
+
this.isDraggingStrokes = true; // We reuse this flag for general dragging
|
|
174
|
+
this.isDraggingOMD = true; // Also set this for OMD elements
|
|
175
|
+
this.hasSeparatedForDrag = false;
|
|
176
|
+
this.dragStartPoint = { x: event.x, y: event.y };
|
|
177
|
+
this.startPoint = { x: event.x, y: event.y }; // For OMD dragging
|
|
178
|
+
|
|
179
|
+
if (this.canvas.eventManager) {
|
|
180
|
+
this.canvas.eventManager.isDrawing = true;
|
|
181
|
+
}
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
137
185
|
// Clicking on empty space - clear all selections and start box selection
|
|
138
186
|
this.resizeHandleManager.clearSelection();
|
|
187
|
+
this.selectedOMDElements.clear();
|
|
139
188
|
this._startBoxSelection(event.x, event.y, event.shiftKey);
|
|
140
189
|
|
|
141
190
|
// CRITICAL: Set startPoint AFTER _startBoxSelection so it doesn't get cleared!
|
|
@@ -159,10 +208,12 @@ export class SelectTool extends Tool {
|
|
|
159
208
|
return;
|
|
160
209
|
}
|
|
161
210
|
|
|
211
|
+
let handled = false;
|
|
212
|
+
|
|
162
213
|
// Handle OMD dragging if in progress
|
|
163
|
-
if (this.isDraggingOMD
|
|
164
|
-
this.
|
|
165
|
-
|
|
214
|
+
if (this.isDraggingOMD) {
|
|
215
|
+
this._dragOMDElements(event.x, event.y);
|
|
216
|
+
handled = true;
|
|
166
217
|
}
|
|
167
218
|
|
|
168
219
|
// Handle stroke dragging if in progress
|
|
@@ -170,38 +221,40 @@ export class SelectTool extends Tool {
|
|
|
170
221
|
const dx = event.x - this.dragStartPoint.x;
|
|
171
222
|
const dy = event.y - this.dragStartPoint.y;
|
|
172
223
|
|
|
173
|
-
if (dx
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
this.potentialDeselect = null;
|
|
224
|
+
if (dx !== 0 || dy !== 0) {
|
|
225
|
+
// If we moved, it's a drag, so cancel potential deselect
|
|
226
|
+
this.potentialDeselect = null;
|
|
177
227
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
228
|
+
// Separate selected parts if needed
|
|
229
|
+
if (!this.hasSeparatedForDrag) {
|
|
230
|
+
this._separateSelectedParts();
|
|
231
|
+
this.hasSeparatedForDrag = true;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Move all selected strokes
|
|
235
|
+
const movedStrokes = new Set();
|
|
236
|
+
for (const [strokeId, _] of this.selectedSegments) {
|
|
237
|
+
const stroke = this.canvas.strokes.get(strokeId);
|
|
238
|
+
if (stroke) {
|
|
239
|
+
stroke.move(dx, dy);
|
|
240
|
+
movedStrokes.add(strokeId);
|
|
241
|
+
}
|
|
191
242
|
}
|
|
243
|
+
|
|
244
|
+
this.dragStartPoint = { x: event.x, y: event.y };
|
|
245
|
+
this._updateSegmentSelectionVisuals();
|
|
246
|
+
|
|
247
|
+
// Emit event
|
|
248
|
+
this.canvas.emit('strokesMoved', {
|
|
249
|
+
dx, dy,
|
|
250
|
+
strokeIds: Array.from(movedStrokes)
|
|
251
|
+
});
|
|
192
252
|
}
|
|
193
|
-
|
|
194
|
-
this.dragStartPoint = { x: event.x, y: event.y };
|
|
195
|
-
this._updateSegmentSelectionVisuals();
|
|
196
|
-
|
|
197
|
-
// Emit event
|
|
198
|
-
this.canvas.emit('strokesMoved', {
|
|
199
|
-
dx, dy,
|
|
200
|
-
strokeIds: Array.from(movedStrokes)
|
|
201
|
-
});
|
|
202
|
-
return;
|
|
253
|
+
handled = true;
|
|
203
254
|
}
|
|
204
255
|
|
|
256
|
+
if (handled) return;
|
|
257
|
+
|
|
205
258
|
// Handle box selection if in progress
|
|
206
259
|
if (!this.isSelecting || !this.selectionBox) return;
|
|
207
260
|
|
|
@@ -389,6 +442,7 @@ export class SelectTool extends Tool {
|
|
|
389
442
|
this.selectedSegments.clear();
|
|
390
443
|
|
|
391
444
|
// Clear OMD selection
|
|
445
|
+
this.selectedOMDElements.clear();
|
|
392
446
|
this.resizeHandleManager.clearSelection();
|
|
393
447
|
|
|
394
448
|
// Remove selection box if it exists
|
|
@@ -494,19 +548,97 @@ export class SelectTool extends Tool {
|
|
|
494
548
|
}
|
|
495
549
|
|
|
496
550
|
/**
|
|
497
|
-
*
|
|
551
|
+
* Gets the bounding box of the current selection (strokes + OMD).
|
|
552
|
+
* @private
|
|
553
|
+
* @returns {{x: number, y: number, width: number, height: number}|null}
|
|
554
|
+
*/
|
|
555
|
+
_getSelectionBounds() {
|
|
556
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
557
|
+
let hasPoints = false;
|
|
558
|
+
|
|
559
|
+
// 1. Check strokes
|
|
560
|
+
if (this.selectedSegments.size > 0) {
|
|
561
|
+
for (const [strokeId, segmentSet] of this.selectedSegments.entries()) {
|
|
562
|
+
const stroke = this.canvas.strokes.get(strokeId);
|
|
563
|
+
if (!stroke || !stroke.points) continue;
|
|
564
|
+
|
|
565
|
+
for (const segmentIndex of segmentSet) {
|
|
566
|
+
if (segmentIndex >= stroke.points.length - 1) continue;
|
|
567
|
+
const p1 = stroke.points[segmentIndex];
|
|
568
|
+
const p2 = stroke.points[segmentIndex + 1];
|
|
569
|
+
|
|
570
|
+
minX = Math.min(minX, p1.x, p2.x);
|
|
571
|
+
minY = Math.min(minY, p1.y, p2.y);
|
|
572
|
+
maxX = Math.max(maxX, p1.x, p2.x);
|
|
573
|
+
maxY = Math.max(maxY, p1.y, p2.y);
|
|
574
|
+
hasPoints = true;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// 2. Check OMD elements
|
|
580
|
+
if (this.selectedOMDElements.size > 0) {
|
|
581
|
+
for (const element of this.selectedOMDElements) {
|
|
582
|
+
const bbox = this._getOMDElementBounds(element);
|
|
583
|
+
if (bbox) {
|
|
584
|
+
minX = Math.min(minX, bbox.x);
|
|
585
|
+
minY = Math.min(minY, bbox.y);
|
|
586
|
+
maxX = Math.max(maxX, bbox.x + bbox.width);
|
|
587
|
+
maxY = Math.max(maxY, bbox.y + bbox.height);
|
|
588
|
+
hasPoints = true;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
if (!hasPoints) return null;
|
|
594
|
+
|
|
595
|
+
// Add padding to match the visual box
|
|
596
|
+
const padding = 8;
|
|
597
|
+
return {
|
|
598
|
+
x: minX - padding,
|
|
599
|
+
y: minY - padding,
|
|
600
|
+
width: (maxX + padding) - (minX - padding),
|
|
601
|
+
height: (maxY + padding) - (minY - padding)
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* Drags all selected OMD elements
|
|
498
607
|
* @private
|
|
499
608
|
* @param {number} x - Current pointer x coordinate
|
|
500
609
|
* @param {number} y - Current pointer y coordinate
|
|
501
610
|
*/
|
|
502
|
-
|
|
503
|
-
if (!this.
|
|
611
|
+
_dragOMDElements(x, y) {
|
|
612
|
+
if (!this.startPoint) return;
|
|
504
613
|
|
|
505
614
|
const dx = x - this.startPoint.x;
|
|
506
615
|
const dy = y - this.startPoint.y;
|
|
507
616
|
|
|
617
|
+
if (dx === 0 && dy === 0) return;
|
|
618
|
+
|
|
619
|
+
for (const element of this.selectedOMDElements) {
|
|
620
|
+
this._moveOMDElement(element, dx, dy);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// Update start point for next move
|
|
624
|
+
this.startPoint = { x, y };
|
|
625
|
+
|
|
626
|
+
// Update resize handles if we have a single selection
|
|
627
|
+
if (this.selectedOMDElements.size === 1) {
|
|
628
|
+
const element = this.selectedOMDElements.values().next().value;
|
|
629
|
+
this.resizeHandleManager.updateIfSelected(element);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
this._updateSegmentSelectionVisuals();
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* Moves a single OMD element
|
|
637
|
+
* @private
|
|
638
|
+
*/
|
|
639
|
+
_moveOMDElement(element, dx, dy) {
|
|
508
640
|
// Parse current transform
|
|
509
|
-
const currentTransform =
|
|
641
|
+
const currentTransform = element.getAttribute('transform') || '';
|
|
510
642
|
const translateMatch = currentTransform.match(/translate\(\s*([^,]+)\s*,\s*([^)]+)\s*\)/);
|
|
511
643
|
const scaleMatch = currentTransform.match(/scale\(\s*([^,)]+)(?:\s*,\s*([^)]+))?\s*\)/);
|
|
512
644
|
|
|
@@ -526,13 +658,40 @@ export class SelectTool extends Tool {
|
|
|
526
658
|
newTransform += ` scale(${scaleX}, ${scaleY})`;
|
|
527
659
|
}
|
|
528
660
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
661
|
+
element.setAttribute('transform', newTransform);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* Gets the bounds of an OMD element including transform
|
|
666
|
+
* @private
|
|
667
|
+
*/
|
|
668
|
+
_getOMDElementBounds(item) {
|
|
669
|
+
try {
|
|
670
|
+
const bbox = item.getBBox();
|
|
671
|
+
const transform = item.getAttribute('transform') || '';
|
|
672
|
+
let offsetX = 0, offsetY = 0, scaleX = 1, scaleY = 1;
|
|
673
|
+
|
|
674
|
+
const translateMatch = transform.match(/translate\(\s*([^,]+)\s*,\s*([^)]+)\s*\)/);
|
|
675
|
+
if (translateMatch) {
|
|
676
|
+
offsetX = parseFloat(translateMatch[1]) || 0;
|
|
677
|
+
offsetY = parseFloat(translateMatch[2]) || 0;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
const scaleMatch = transform.match(/scale\(\s*([^,)]+)(?:\s*,\s*([^)]+))?\s*\)/);
|
|
681
|
+
if (scaleMatch) {
|
|
682
|
+
scaleX = parseFloat(scaleMatch[1]) || 1;
|
|
683
|
+
scaleY = scaleMatch[2] ? parseFloat(scaleMatch[2]) : scaleX;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
return {
|
|
687
|
+
x: offsetX + (bbox.x * scaleX),
|
|
688
|
+
y: offsetY + (bbox.y * scaleY),
|
|
689
|
+
width: bbox.width * scaleX,
|
|
690
|
+
height: bbox.height * scaleY
|
|
691
|
+
};
|
|
692
|
+
} catch (e) {
|
|
693
|
+
return null;
|
|
694
|
+
}
|
|
536
695
|
}
|
|
537
696
|
|
|
538
697
|
/**
|
|
@@ -711,6 +870,7 @@ export class SelectTool extends Tool {
|
|
|
711
870
|
const height = parseFloat(this.selectionBox.getAttribute('height'));
|
|
712
871
|
const selectionBounds = new BoundingBox(x, y, width, height);
|
|
713
872
|
|
|
873
|
+
// 1. Select strokes
|
|
714
874
|
for (const [id, stroke] of this.canvas.strokes) {
|
|
715
875
|
if (!stroke.points || stroke.points.length < 2) continue;
|
|
716
876
|
for (let i = 0; i < stroke.points.length - 1; i++) {
|
|
@@ -724,6 +884,32 @@ export class SelectTool extends Tool {
|
|
|
724
884
|
}
|
|
725
885
|
}
|
|
726
886
|
}
|
|
887
|
+
|
|
888
|
+
// 2. Select OMD elements
|
|
889
|
+
const omdLayer = this.canvas.drawingLayer?.querySelector('.omd-layer');
|
|
890
|
+
if (omdLayer) {
|
|
891
|
+
const omdItems = omdLayer.querySelectorAll('.omd-item');
|
|
892
|
+
for (const item of omdItems) {
|
|
893
|
+
if (item?.dataset?.locked === 'true') continue;
|
|
894
|
+
|
|
895
|
+
const itemBounds = this._getOMDElementBounds(item);
|
|
896
|
+
if (itemBounds) {
|
|
897
|
+
// Check intersection
|
|
898
|
+
const intersects = !(itemBounds.x > x + width ||
|
|
899
|
+
itemBounds.x + itemBounds.width < x ||
|
|
900
|
+
itemBounds.y > y + height ||
|
|
901
|
+
itemBounds.y + itemBounds.height < y);
|
|
902
|
+
|
|
903
|
+
if (intersects) {
|
|
904
|
+
this.selectedOMDElements.add(item);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
// Update resize handles
|
|
911
|
+
this.resizeHandleManager.clearSelection();
|
|
912
|
+
|
|
727
913
|
this._updateSegmentSelectionVisuals();
|
|
728
914
|
}
|
|
729
915
|
|
|
@@ -784,48 +970,15 @@ export class SelectTool extends Tool {
|
|
|
784
970
|
selectionLayer.removeChild(selectionLayer.firstChild);
|
|
785
971
|
}
|
|
786
972
|
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
790
|
-
let hasSelection = false;
|
|
791
|
-
|
|
792
|
-
// Calculate bounding box of all selected segments
|
|
793
|
-
for (const [strokeId, segmentSet] of this.selectedSegments.entries()) {
|
|
794
|
-
const stroke = this.canvas.strokes.get(strokeId);
|
|
795
|
-
if (!stroke || !stroke.points) continue;
|
|
796
|
-
|
|
797
|
-
for (const segmentIndex of segmentSet) {
|
|
798
|
-
if (segmentIndex >= stroke.points.length - 1) continue;
|
|
799
|
-
|
|
800
|
-
const p1 = stroke.points[segmentIndex];
|
|
801
|
-
const p2 = stroke.points[segmentIndex + 1];
|
|
802
|
-
|
|
803
|
-
minX = Math.min(minX, p1.x, p2.x);
|
|
804
|
-
minY = Math.min(minY, p1.y, p2.y);
|
|
805
|
-
maxX = Math.max(maxX, p1.x, p2.x);
|
|
806
|
-
maxY = Math.max(maxY, p1.y, p2.y);
|
|
807
|
-
hasSelection = true;
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
if (!hasSelection) return;
|
|
812
|
-
|
|
813
|
-
// Add padding
|
|
814
|
-
const padding = 8;
|
|
815
|
-
minX -= padding;
|
|
816
|
-
minY -= padding;
|
|
817
|
-
maxX += padding;
|
|
818
|
-
maxY += padding;
|
|
819
|
-
|
|
820
|
-
const width = maxX - minX;
|
|
821
|
-
const height = maxY - minY;
|
|
973
|
+
const bounds = this._getSelectionBounds();
|
|
974
|
+
if (!bounds) return;
|
|
822
975
|
|
|
823
976
|
// Create bounding box element
|
|
824
977
|
const box = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
|
825
|
-
box.setAttribute('x',
|
|
826
|
-
box.setAttribute('y',
|
|
827
|
-
box.setAttribute('width', width);
|
|
828
|
-
box.setAttribute('height', height);
|
|
978
|
+
box.setAttribute('x', bounds.x);
|
|
979
|
+
box.setAttribute('y', bounds.y);
|
|
980
|
+
box.setAttribute('width', bounds.width);
|
|
981
|
+
box.setAttribute('height', bounds.height);
|
|
829
982
|
box.setAttribute('fill', 'none');
|
|
830
983
|
box.setAttribute('stroke', '#007bff'); // Selection color
|
|
831
984
|
box.setAttribute('stroke-width', '1.5');
|
|
@@ -850,12 +1003,13 @@ export class SelectTool extends Tool {
|
|
|
850
1003
|
}
|
|
851
1004
|
|
|
852
1005
|
/**
|
|
853
|
-
* Selects all segments on the canvas.
|
|
1006
|
+
* Selects all segments and OMD elements on the canvas.
|
|
854
1007
|
* @private
|
|
855
1008
|
*/
|
|
856
1009
|
_selectAllSegments() {
|
|
857
1010
|
// Clear current selection
|
|
858
1011
|
this.selectedSegments.clear();
|
|
1012
|
+
this.selectedOMDElements.clear();
|
|
859
1013
|
|
|
860
1014
|
let totalSegments = 0;
|
|
861
1015
|
|
|
@@ -874,6 +1028,17 @@ export class SelectTool extends Tool {
|
|
|
874
1028
|
}
|
|
875
1029
|
}
|
|
876
1030
|
|
|
1031
|
+
// Select all OMD elements
|
|
1032
|
+
const omdLayer = this.canvas.drawingLayer?.querySelector('.omd-layer');
|
|
1033
|
+
if (omdLayer) {
|
|
1034
|
+
const omdItems = omdLayer.querySelectorAll('.omd-item');
|
|
1035
|
+
for (const item of omdItems) {
|
|
1036
|
+
if (item?.dataset?.locked !== 'true') {
|
|
1037
|
+
this.selectedOMDElements.add(item);
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
|
|
877
1042
|
// Update visuals
|
|
878
1043
|
this._updateSegmentSelectionVisuals();
|
|
879
1044
|
|
|
@@ -885,42 +1050,55 @@ export class SelectTool extends Tool {
|
|
|
885
1050
|
}
|
|
886
1051
|
|
|
887
1052
|
/**
|
|
888
|
-
* Deletes all currently selected segments
|
|
1053
|
+
* Deletes all currently selected items (segments and OMD elements).
|
|
889
1054
|
* @private
|
|
890
1055
|
*/
|
|
891
1056
|
_deleteSelectedSegments() {
|
|
892
|
-
|
|
1057
|
+
let changed = false;
|
|
893
1058
|
|
|
894
|
-
//
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
1059
|
+
// Delete OMD elements
|
|
1060
|
+
if (this.selectedOMDElements.size > 0) {
|
|
1061
|
+
for (const element of this.selectedOMDElements) {
|
|
1062
|
+
element.remove();
|
|
1063
|
+
}
|
|
1064
|
+
this.selectedOMDElements.clear();
|
|
1065
|
+
this.resizeHandleManager.clearSelection();
|
|
1066
|
+
changed = true;
|
|
1067
|
+
}
|
|
900
1068
|
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
const totalSegments = stroke.points.length - 1;
|
|
905
|
-
const selectedCount = sortedIndices.length;
|
|
1069
|
+
if (this.selectedSegments.size > 0) {
|
|
1070
|
+
// Process each stroke that has selected segments
|
|
1071
|
+
const strokesToProcess = Array.from(this.selectedSegments.entries());
|
|
906
1072
|
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
continue;
|
|
911
|
-
}
|
|
1073
|
+
for (const [strokeId, segmentIndices] of strokesToProcess) {
|
|
1074
|
+
const stroke = this.canvas.strokes.get(strokeId);
|
|
1075
|
+
if (!stroke || !stroke.points || stroke.points.length < 2) continue;
|
|
912
1076
|
|
|
913
|
-
|
|
914
|
-
|
|
1077
|
+
const sortedIndices = Array.from(segmentIndices).sort((a, b) => a - b);
|
|
1078
|
+
|
|
1079
|
+
// If all or most segments are selected, just delete the whole stroke
|
|
1080
|
+
const totalSegments = stroke.points.length - 1;
|
|
1081
|
+
const selectedCount = sortedIndices.length;
|
|
1082
|
+
|
|
1083
|
+
if (selectedCount >= totalSegments * 0.8) {
|
|
1084
|
+
// Delete entire stroke
|
|
1085
|
+
this.canvas.removeStroke(strokeId);
|
|
1086
|
+
continue;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
// Split the stroke, keeping only unselected segments
|
|
1090
|
+
this._splitStrokeKeepingUnselected(stroke, sortedIndices);
|
|
1091
|
+
}
|
|
1092
|
+
changed = true;
|
|
915
1093
|
}
|
|
916
1094
|
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
}
|
|
1095
|
+
if (changed) {
|
|
1096
|
+
// Clear selection and update UI
|
|
1097
|
+
this.clearSelection();
|
|
1098
|
+
|
|
1099
|
+
// Emit deletion event
|
|
1100
|
+
this.canvas.emit('selectionDeleted');
|
|
1101
|
+
}
|
|
924
1102
|
}
|
|
925
1103
|
|
|
926
1104
|
/**
|