@cornerstonejs/tools 1.21.1 → 1.22.0
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/cjs/eventDispatchers/mouseEventHandlers/mouseDown.js +1 -1
- package/dist/cjs/eventDispatchers/mouseEventHandlers/mouseDown.js.map +1 -1
- package/dist/cjs/tools/MagnifyTool.js +2 -2
- package/dist/cjs/tools/MagnifyTool.js.map +1 -1
- package/dist/cjs/tools/annotation/CobbAngleTool.d.ts +31 -5
- package/dist/cjs/tools/annotation/CobbAngleTool.js +266 -70
- package/dist/cjs/tools/annotation/CobbAngleTool.js.map +1 -1
- package/dist/cjs/tools/base/AnnotationTool.d.ts +1 -1
- package/dist/cjs/tools/base/AnnotationTool.js.map +1 -1
- package/dist/cjs/tools/displayTools/Contour/contourDisplay.js.map +1 -1
- package/dist/cjs/types/ToolSpecificAnnotationTypes.d.ts +44 -0
- package/dist/cjs/utilities/math/angle/angleBetweenLines.d.ts +3 -1
- package/dist/cjs/utilities/math/angle/angleBetweenLines.js +18 -1
- package/dist/cjs/utilities/math/angle/angleBetweenLines.js.map +1 -1
- package/dist/esm/eventDispatchers/mouseEventHandlers/mouseDown.js +1 -1
- package/dist/esm/eventDispatchers/mouseEventHandlers/mouseDown.js.map +1 -1
- package/dist/esm/tools/MagnifyTool.js +2 -2
- package/dist/esm/tools/MagnifyTool.js.map +1 -1
- package/dist/esm/tools/annotation/CobbAngleTool.d.ts +31 -5
- package/dist/esm/tools/annotation/CobbAngleTool.js +273 -71
- package/dist/esm/tools/annotation/CobbAngleTool.js.map +1 -1
- package/dist/esm/tools/base/AnnotationTool.d.ts +1 -1
- package/dist/esm/tools/base/AnnotationTool.js.map +1 -1
- package/dist/esm/tools/displayTools/Contour/contourDisplay.js.map +1 -1
- package/dist/esm/types/ToolSpecificAnnotationTypes.d.ts +44 -0
- package/dist/esm/utilities/math/angle/angleBetweenLines.d.ts +3 -1
- package/dist/esm/utilities/math/angle/angleBetweenLines.js +19 -2
- package/dist/esm/utilities/math/angle/angleBetweenLines.js.map +1 -1
- package/dist/umd/index.js +1 -1
- package/dist/umd/index.js.map +1 -1
- package/package.json +3 -3
- package/src/eventDispatchers/mouseEventHandlers/mouseDown.ts +1 -1
- package/src/tools/MagnifyTool.ts +2 -2
- package/src/tools/annotation/CobbAngleTool.ts +418 -98
- package/src/tools/base/AnnotationTool.ts +2 -1
- package/src/tools/displayTools/Contour/contourDisplay.ts +0 -1
- package/src/types/ToolSpecificAnnotationTypes.ts +46 -0
- package/src/utilities/math/angle/angleBetweenLines.ts +39 -9
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
drawHandles as drawHandlesSvg,
|
|
24
24
|
drawLine as drawLineSvg,
|
|
25
25
|
drawLinkedTextBox as drawLinkedTextBoxSvg,
|
|
26
|
+
drawTextBox as drawTextBoxSvg,
|
|
26
27
|
} from '../../drawingSvg';
|
|
27
28
|
import { state } from '../../store';
|
|
28
29
|
import { getViewportIdsWithToolToRender } from '../../utilities/viewportFilters';
|
|
@@ -47,7 +48,7 @@ import {
|
|
|
47
48
|
InteractionTypes,
|
|
48
49
|
SVGDrawingHelper,
|
|
49
50
|
} from '../../types';
|
|
50
|
-
import {
|
|
51
|
+
import { CobbAngleAnnotation } from '../../types/ToolSpecificAnnotationTypes';
|
|
51
52
|
import { StyleSpecifier } from '../../types/AnnotationStyle';
|
|
52
53
|
|
|
53
54
|
class CobbAngleTool extends AnnotationTool {
|
|
@@ -64,6 +65,8 @@ class CobbAngleTool extends AnnotationTool {
|
|
|
64
65
|
movingTextBox?: boolean;
|
|
65
66
|
newAnnotation?: boolean;
|
|
66
67
|
hasMoved?: boolean;
|
|
68
|
+
isNearFirstLine?: boolean;
|
|
69
|
+
isNearSecondLine?: boolean;
|
|
67
70
|
} | null;
|
|
68
71
|
isDrawing: boolean;
|
|
69
72
|
isHandleOutsideImage: boolean;
|
|
@@ -83,7 +86,7 @@ class CobbAngleTool extends AnnotationTool {
|
|
|
83
86
|
|
|
84
87
|
this._throttledCalculateCachedStats = throttle(
|
|
85
88
|
this._calculateCachedStats,
|
|
86
|
-
|
|
89
|
+
25,
|
|
87
90
|
{ trailing: true }
|
|
88
91
|
);
|
|
89
92
|
}
|
|
@@ -98,7 +101,7 @@ class CobbAngleTool extends AnnotationTool {
|
|
|
98
101
|
*/
|
|
99
102
|
addNewAnnotation = (
|
|
100
103
|
evt: EventTypes.MouseDownActivateEventType
|
|
101
|
-
):
|
|
104
|
+
): CobbAngleAnnotation => {
|
|
102
105
|
if (this.angleStartedNotYetCompleted) {
|
|
103
106
|
return;
|
|
104
107
|
}
|
|
@@ -192,52 +195,20 @@ class CobbAngleTool extends AnnotationTool {
|
|
|
192
195
|
*/
|
|
193
196
|
isPointNearTool = (
|
|
194
197
|
element: HTMLDivElement,
|
|
195
|
-
annotation:
|
|
198
|
+
annotation: CobbAngleAnnotation,
|
|
196
199
|
canvasCoords: Types.Point2,
|
|
197
200
|
proximity: number
|
|
198
201
|
): boolean => {
|
|
199
202
|
const enabledElement = getEnabledElement(element);
|
|
200
203
|
const { viewport } = enabledElement;
|
|
201
204
|
const { data } = annotation;
|
|
202
|
-
const [point1, point2, point3, point4] = data.handles.points;
|
|
203
|
-
const canvasPoint1 = viewport.worldToCanvas(point1);
|
|
204
|
-
const canvasPoint2 = viewport.worldToCanvas(point2);
|
|
205
|
-
const canvasPoint3 = viewport.worldToCanvas(point3);
|
|
206
|
-
const canvasPoint4 = viewport.worldToCanvas(point4);
|
|
207
|
-
|
|
208
|
-
const line1 = {
|
|
209
|
-
start: {
|
|
210
|
-
x: canvasPoint1[0],
|
|
211
|
-
y: canvasPoint1[1],
|
|
212
|
-
},
|
|
213
|
-
end: {
|
|
214
|
-
x: canvasPoint2[0],
|
|
215
|
-
y: canvasPoint2[1],
|
|
216
|
-
},
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
const line2 = {
|
|
220
|
-
start: {
|
|
221
|
-
x: canvasPoint3[0],
|
|
222
|
-
y: canvasPoint3[1],
|
|
223
|
-
},
|
|
224
|
-
end: {
|
|
225
|
-
x: canvasPoint4[0],
|
|
226
|
-
y: canvasPoint4[1],
|
|
227
|
-
},
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
const distanceToPoint = lineSegment.distanceToPoint(
|
|
231
|
-
[line1.start.x, line1.start.y],
|
|
232
|
-
[line1.end.x, line1.end.y],
|
|
233
|
-
[canvasCoords[0], canvasCoords[1]]
|
|
234
|
-
);
|
|
235
205
|
|
|
236
|
-
const distanceToPoint2 =
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
206
|
+
const { distanceToPoint, distanceToPoint2 } = this.distanceToLines({
|
|
207
|
+
viewport,
|
|
208
|
+
points: data.handles.points,
|
|
209
|
+
canvasCoords,
|
|
210
|
+
proximity,
|
|
211
|
+
});
|
|
241
212
|
|
|
242
213
|
if (distanceToPoint <= proximity || distanceToPoint2 <= proximity) {
|
|
243
214
|
return true;
|
|
@@ -248,8 +219,10 @@ class CobbAngleTool extends AnnotationTool {
|
|
|
248
219
|
|
|
249
220
|
toolSelectedCallback = (
|
|
250
221
|
evt: EventTypes.MouseDownEventType,
|
|
251
|
-
annotation:
|
|
252
|
-
interactionType: InteractionTypes
|
|
222
|
+
annotation: CobbAngleAnnotation,
|
|
223
|
+
interactionType: InteractionTypes,
|
|
224
|
+
canvasCoords: Types.Point2,
|
|
225
|
+
proximity = 6
|
|
253
226
|
): void => {
|
|
254
227
|
const eventDetail = evt.detail;
|
|
255
228
|
const { element } = eventDetail;
|
|
@@ -261,19 +234,28 @@ class CobbAngleTool extends AnnotationTool {
|
|
|
261
234
|
this.getToolName()
|
|
262
235
|
);
|
|
263
236
|
|
|
237
|
+
const enabledElement = getEnabledElement(element);
|
|
238
|
+
const { renderingEngine, viewport } = enabledElement;
|
|
239
|
+
|
|
240
|
+
const { isNearFirstLine, isNearSecondLine } = this.distanceToLines({
|
|
241
|
+
viewport,
|
|
242
|
+
points: annotation.data.handles.points,
|
|
243
|
+
canvasCoords,
|
|
244
|
+
proximity,
|
|
245
|
+
});
|
|
246
|
+
|
|
264
247
|
this.editData = {
|
|
265
248
|
annotation,
|
|
266
249
|
viewportIdsToRender,
|
|
267
250
|
movingTextBox: false,
|
|
251
|
+
isNearFirstLine,
|
|
252
|
+
isNearSecondLine,
|
|
268
253
|
};
|
|
269
254
|
|
|
270
255
|
this._activateModify(element);
|
|
271
256
|
|
|
272
257
|
hideElementCursor(element);
|
|
273
258
|
|
|
274
|
-
const enabledElement = getEnabledElement(element);
|
|
275
|
-
const { renderingEngine } = enabledElement;
|
|
276
|
-
|
|
277
259
|
triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
|
|
278
260
|
|
|
279
261
|
evt.preventDefault();
|
|
@@ -281,7 +263,7 @@ class CobbAngleTool extends AnnotationTool {
|
|
|
281
263
|
|
|
282
264
|
handleSelectedCallback(
|
|
283
265
|
evt: EventTypes.MouseDownEventType,
|
|
284
|
-
annotation:
|
|
266
|
+
annotation: CobbAngleAnnotation,
|
|
285
267
|
handle: ToolHandle,
|
|
286
268
|
interactionType = 'mouse'
|
|
287
269
|
): void {
|
|
@@ -436,8 +418,14 @@ class CobbAngleTool extends AnnotationTool {
|
|
|
436
418
|
const eventDetail = evt.detail;
|
|
437
419
|
const { element } = eventDetail;
|
|
438
420
|
|
|
439
|
-
const {
|
|
440
|
-
|
|
421
|
+
const {
|
|
422
|
+
annotation,
|
|
423
|
+
viewportIdsToRender,
|
|
424
|
+
handleIndex,
|
|
425
|
+
movingTextBox,
|
|
426
|
+
isNearFirstLine,
|
|
427
|
+
isNearSecondLine,
|
|
428
|
+
} = this.editData;
|
|
441
429
|
const { data } = annotation;
|
|
442
430
|
|
|
443
431
|
if (movingTextBox) {
|
|
@@ -453,21 +441,35 @@ class CobbAngleTool extends AnnotationTool {
|
|
|
453
441
|
worldPosition[2] += worldPosDelta[2];
|
|
454
442
|
|
|
455
443
|
textBox.hasMoved = true;
|
|
456
|
-
} else if (
|
|
457
|
-
|
|
444
|
+
} else if (
|
|
445
|
+
handleIndex === undefined &&
|
|
446
|
+
(isNearFirstLine || isNearSecondLine)
|
|
447
|
+
) {
|
|
448
|
+
// select tool mode - moving annotation
|
|
458
449
|
const { deltaPoints } = eventDetail as EventTypes.MouseDragEventDetail;
|
|
459
450
|
const worldPosDelta = deltaPoints.world;
|
|
460
|
-
|
|
461
451
|
const points = data.handles.points;
|
|
462
452
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
point
|
|
467
|
-
|
|
453
|
+
// separate the logic for moving handles to move them separately
|
|
454
|
+
if (isNearFirstLine) {
|
|
455
|
+
const firstLinePoints = [points[0], points[1]];
|
|
456
|
+
firstLinePoints.forEach((point) => {
|
|
457
|
+
point[0] += worldPosDelta[0];
|
|
458
|
+
point[1] += worldPosDelta[1];
|
|
459
|
+
point[2] += worldPosDelta[2];
|
|
460
|
+
});
|
|
461
|
+
} else if (isNearSecondLine) {
|
|
462
|
+
const secondLinePoints = [points[2], points[3]];
|
|
463
|
+
secondLinePoints.forEach((point) => {
|
|
464
|
+
point[0] += worldPosDelta[0];
|
|
465
|
+
point[1] += worldPosDelta[1];
|
|
466
|
+
point[2] += worldPosDelta[2];
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
|
|
468
470
|
annotation.invalidated = true;
|
|
469
471
|
} else {
|
|
470
|
-
//
|
|
472
|
+
// Drag handle mode - after double click, and mouse move to draw
|
|
471
473
|
const { currentPoints } = eventDetail;
|
|
472
474
|
const worldPos = currentPoints.world;
|
|
473
475
|
|
|
@@ -485,40 +487,44 @@ class CobbAngleTool extends AnnotationTool {
|
|
|
485
487
|
|
|
486
488
|
cancel = (element: HTMLDivElement) => {
|
|
487
489
|
// If it is mid-draw or mid-modify
|
|
488
|
-
if (this.isDrawing) {
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
this._deactivateModify(element);
|
|
492
|
-
resetElementCursor(element);
|
|
490
|
+
if (!this.isDrawing) {
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
493
|
|
|
494
|
-
|
|
495
|
-
|
|
494
|
+
this.isDrawing = false;
|
|
495
|
+
this._deactivateDraw(element);
|
|
496
|
+
this._deactivateModify(element);
|
|
497
|
+
resetElementCursor(element);
|
|
496
498
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
+
const { annotation, viewportIdsToRender, newAnnotation } = this.editData;
|
|
500
|
+
const { data } = annotation;
|
|
499
501
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
+
if (data.handles.points.length < 4) {
|
|
503
|
+
// If it is mid-draw
|
|
504
|
+
removeAnnotation(annotation.annotationUID);
|
|
505
|
+
}
|
|
502
506
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
viewportIdsToRender
|
|
506
|
-
);
|
|
507
|
+
annotation.highlighted = false;
|
|
508
|
+
data.handles.activeHandleIndex = null;
|
|
507
509
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
+
const enabledElement = getEnabledElement(element);
|
|
511
|
+
const { renderingEngine } = enabledElement;
|
|
510
512
|
|
|
511
|
-
|
|
512
|
-
annotation,
|
|
513
|
-
};
|
|
513
|
+
triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
|
|
514
514
|
|
|
515
|
-
|
|
516
|
-
|
|
515
|
+
if (newAnnotation) {
|
|
516
|
+
const eventType = Events.ANNOTATION_COMPLETED;
|
|
517
517
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
518
|
+
const eventDetail: AnnotationCompletedEventDetail = {
|
|
519
|
+
annotation,
|
|
520
|
+
};
|
|
521
|
+
|
|
522
|
+
triggerEvent(eventTarget, eventType, eventDetail);
|
|
521
523
|
}
|
|
524
|
+
|
|
525
|
+
this.editData = null;
|
|
526
|
+
this.angleStartedNotYetCompleted = false;
|
|
527
|
+
return annotation.annotationUID;
|
|
522
528
|
};
|
|
523
529
|
|
|
524
530
|
_activateModify = (element: HTMLDivElement) => {
|
|
@@ -661,7 +667,7 @@ class CobbAngleTool extends AnnotationTool {
|
|
|
661
667
|
|
|
662
668
|
// Draw SVG
|
|
663
669
|
for (let i = 0; i < annotations.length; i++) {
|
|
664
|
-
const annotation = annotations[i] as
|
|
670
|
+
const annotation = annotations[i] as CobbAngleAnnotation;
|
|
665
671
|
const { annotationUID, data } = annotation;
|
|
666
672
|
const { points, activeHandleIndex } = data.handles;
|
|
667
673
|
|
|
@@ -680,6 +686,26 @@ class CobbAngleTool extends AnnotationTool {
|
|
|
680
686
|
) {
|
|
681
687
|
data.cachedStats[targetId] = {
|
|
682
688
|
angle: null,
|
|
689
|
+
arc1Angle: null,
|
|
690
|
+
arc2Angle: null,
|
|
691
|
+
points: {
|
|
692
|
+
world: {
|
|
693
|
+
arc1Start: null,
|
|
694
|
+
arc1End: null,
|
|
695
|
+
arc2Start: null,
|
|
696
|
+
arc2End: null,
|
|
697
|
+
arc1Angle: null,
|
|
698
|
+
arc2Angle: null,
|
|
699
|
+
},
|
|
700
|
+
canvas: {
|
|
701
|
+
arc1Start: null,
|
|
702
|
+
arc1End: null,
|
|
703
|
+
arc2Start: null,
|
|
704
|
+
arc2End: null,
|
|
705
|
+
arc1Angle: null,
|
|
706
|
+
arc2Angle: null,
|
|
707
|
+
},
|
|
708
|
+
},
|
|
683
709
|
};
|
|
684
710
|
|
|
685
711
|
this._calculateCachedStats(annotation, renderingEngine, enabledElement);
|
|
@@ -724,13 +750,22 @@ class CobbAngleTool extends AnnotationTool {
|
|
|
724
750
|
);
|
|
725
751
|
}
|
|
726
752
|
|
|
727
|
-
|
|
753
|
+
const firstLine = [canvasCoordinates[0], canvasCoordinates[1]] as [
|
|
754
|
+
Types.Point2,
|
|
755
|
+
Types.Point2
|
|
756
|
+
];
|
|
757
|
+
const secondLine = [canvasCoordinates[2], canvasCoordinates[3]] as [
|
|
758
|
+
Types.Point2,
|
|
759
|
+
Types.Point2
|
|
760
|
+
];
|
|
761
|
+
|
|
762
|
+
let lineUID = 'line1';
|
|
728
763
|
drawLineSvg(
|
|
729
764
|
svgDrawingHelper,
|
|
730
765
|
annotationUID,
|
|
731
766
|
lineUID,
|
|
732
|
-
|
|
733
|
-
|
|
767
|
+
firstLine[0],
|
|
768
|
+
firstLine[1],
|
|
734
769
|
{
|
|
735
770
|
color,
|
|
736
771
|
width: lineWidth,
|
|
@@ -740,19 +775,19 @@ class CobbAngleTool extends AnnotationTool {
|
|
|
740
775
|
|
|
741
776
|
renderStatus = true;
|
|
742
777
|
|
|
743
|
-
// Don't add
|
|
778
|
+
// Don't add the stats until annotation has 4 anchor points
|
|
744
779
|
if (canvasCoordinates.length < 4) {
|
|
745
780
|
return renderStatus;
|
|
746
781
|
}
|
|
747
782
|
|
|
748
|
-
lineUID = '
|
|
783
|
+
lineUID = 'line2';
|
|
749
784
|
|
|
750
785
|
drawLineSvg(
|
|
751
786
|
svgDrawingHelper,
|
|
752
787
|
annotationUID,
|
|
753
788
|
lineUID,
|
|
754
|
-
|
|
755
|
-
|
|
789
|
+
secondLine[0],
|
|
790
|
+
secondLine[1],
|
|
756
791
|
{
|
|
757
792
|
color,
|
|
758
793
|
width: lineWidth,
|
|
@@ -760,15 +795,49 @@ class CobbAngleTool extends AnnotationTool {
|
|
|
760
795
|
}
|
|
761
796
|
);
|
|
762
797
|
|
|
763
|
-
lineUID = '
|
|
764
|
-
const mid1 = midPoint2(
|
|
765
|
-
const mid2 = midPoint2(
|
|
798
|
+
lineUID = 'linkLine';
|
|
799
|
+
const mid1 = midPoint2(firstLine[0], firstLine[1]);
|
|
800
|
+
const mid2 = midPoint2(secondLine[0], secondLine[1]);
|
|
766
801
|
drawLineSvg(svgDrawingHelper, annotationUID, lineUID, mid1, mid2, {
|
|
767
802
|
color,
|
|
768
803
|
lineWidth: '1',
|
|
769
804
|
lineDash: '1,4',
|
|
770
805
|
});
|
|
771
806
|
|
|
807
|
+
// Calculating the arcs
|
|
808
|
+
|
|
809
|
+
const { arc1Start, arc1End, arc2End, arc2Start } =
|
|
810
|
+
data.cachedStats[targetId].points.canvas;
|
|
811
|
+
const { arc1Angle, arc2Angle } = data.cachedStats[targetId];
|
|
812
|
+
|
|
813
|
+
lineUID = 'arc1';
|
|
814
|
+
|
|
815
|
+
drawLineSvg(
|
|
816
|
+
svgDrawingHelper,
|
|
817
|
+
annotationUID,
|
|
818
|
+
lineUID,
|
|
819
|
+
arc1Start as Types.Point2,
|
|
820
|
+
arc1End as Types.Point2,
|
|
821
|
+
{
|
|
822
|
+
color,
|
|
823
|
+
lineWidth: '1',
|
|
824
|
+
}
|
|
825
|
+
);
|
|
826
|
+
|
|
827
|
+
lineUID = 'arc2';
|
|
828
|
+
|
|
829
|
+
drawLineSvg(
|
|
830
|
+
svgDrawingHelper,
|
|
831
|
+
annotationUID,
|
|
832
|
+
lineUID,
|
|
833
|
+
arc2Start as Types.Point2,
|
|
834
|
+
arc2End as Types.Point2,
|
|
835
|
+
{
|
|
836
|
+
color,
|
|
837
|
+
lineWidth: '1',
|
|
838
|
+
}
|
|
839
|
+
);
|
|
840
|
+
|
|
772
841
|
if (!data.cachedStats[targetId]?.angle) {
|
|
773
842
|
continue;
|
|
774
843
|
}
|
|
@@ -801,7 +870,7 @@ class CobbAngleTool extends AnnotationTool {
|
|
|
801
870
|
data.handles.textBox.worldPosition
|
|
802
871
|
);
|
|
803
872
|
|
|
804
|
-
const textBoxUID = '
|
|
873
|
+
const textBoxUID = 'cobbAngleText';
|
|
805
874
|
const boundingBox = drawLinkedTextBoxSvg(
|
|
806
875
|
svgDrawingHelper,
|
|
807
876
|
annotationUID,
|
|
@@ -821,6 +890,46 @@ class CobbAngleTool extends AnnotationTool {
|
|
|
821
890
|
bottomLeft: viewport.canvasToWorld([left, top + height]),
|
|
822
891
|
bottomRight: viewport.canvasToWorld([left + width, top + height]),
|
|
823
892
|
};
|
|
893
|
+
|
|
894
|
+
const arc1TextBoxUID = 'arcAngle1';
|
|
895
|
+
|
|
896
|
+
const arc1TextLine = [
|
|
897
|
+
`${arc1Angle.toFixed(2)} ${String.fromCharCode(176)}`,
|
|
898
|
+
];
|
|
899
|
+
|
|
900
|
+
const arch1TextPosCanvas = midPoint2(arc1Start, arc1End);
|
|
901
|
+
|
|
902
|
+
drawTextBoxSvg(
|
|
903
|
+
svgDrawingHelper,
|
|
904
|
+
annotationUID,
|
|
905
|
+
arc1TextBoxUID,
|
|
906
|
+
arc1TextLine,
|
|
907
|
+
arch1TextPosCanvas,
|
|
908
|
+
{
|
|
909
|
+
...options,
|
|
910
|
+
padding: 3,
|
|
911
|
+
}
|
|
912
|
+
);
|
|
913
|
+
|
|
914
|
+
const arc2TextBoxUID = 'arcAngle2';
|
|
915
|
+
|
|
916
|
+
const arc2TextLine = [
|
|
917
|
+
`${arc2Angle.toFixed(2)} ${String.fromCharCode(176)}`,
|
|
918
|
+
];
|
|
919
|
+
|
|
920
|
+
const arch2TextPosCanvas = midPoint2(arc2Start, arc2End);
|
|
921
|
+
|
|
922
|
+
drawTextBoxSvg(
|
|
923
|
+
svgDrawingHelper,
|
|
924
|
+
annotationUID,
|
|
925
|
+
arc2TextBoxUID,
|
|
926
|
+
arc2TextLine,
|
|
927
|
+
arch2TextPosCanvas,
|
|
928
|
+
{
|
|
929
|
+
...options,
|
|
930
|
+
padding: 3,
|
|
931
|
+
}
|
|
932
|
+
);
|
|
824
933
|
}
|
|
825
934
|
|
|
826
935
|
return renderStatus;
|
|
@@ -862,16 +971,56 @@ class CobbAngleTool extends AnnotationTool {
|
|
|
862
971
|
}
|
|
863
972
|
}
|
|
864
973
|
}
|
|
974
|
+
const { viewport } = enabledElement;
|
|
975
|
+
|
|
976
|
+
const canvasPoints = data.handles.points.map((p) =>
|
|
977
|
+
viewport.worldToCanvas(p)
|
|
978
|
+
);
|
|
979
|
+
|
|
980
|
+
const firstLine = [canvasPoints[0], canvasPoints[1]] as [
|
|
981
|
+
Types.Point2,
|
|
982
|
+
Types.Point2
|
|
983
|
+
];
|
|
984
|
+
const secondLine = [canvasPoints[2], canvasPoints[3]] as [
|
|
985
|
+
Types.Point2,
|
|
986
|
+
Types.Point2
|
|
987
|
+
];
|
|
988
|
+
|
|
989
|
+
const mid1 = midPoint2(firstLine[0], firstLine[1]);
|
|
990
|
+
const mid2 = midPoint2(secondLine[0], secondLine[1]);
|
|
991
|
+
|
|
992
|
+
const { arc1Start, arc1End, arc2End, arc2Start, arc1Angle, arc2Angle } =
|
|
993
|
+
this.getArcsStartEndPoints({
|
|
994
|
+
firstLine,
|
|
995
|
+
secondLine,
|
|
996
|
+
mid1,
|
|
997
|
+
mid2,
|
|
998
|
+
});
|
|
865
999
|
|
|
866
1000
|
const { cachedStats } = data;
|
|
867
1001
|
const targetIds = Object.keys(cachedStats);
|
|
868
1002
|
|
|
869
1003
|
for (let i = 0; i < targetIds.length; i++) {
|
|
870
1004
|
const targetId = targetIds[i];
|
|
871
|
-
const angle = angleBetweenLines(seg1, seg2);
|
|
872
1005
|
|
|
873
1006
|
cachedStats[targetId] = {
|
|
874
|
-
angle,
|
|
1007
|
+
angle: angleBetweenLines(seg1, seg2),
|
|
1008
|
+
arc1Angle,
|
|
1009
|
+
arc2Angle,
|
|
1010
|
+
points: {
|
|
1011
|
+
canvas: {
|
|
1012
|
+
arc1Start,
|
|
1013
|
+
arc1End,
|
|
1014
|
+
arc2End,
|
|
1015
|
+
arc2Start,
|
|
1016
|
+
},
|
|
1017
|
+
world: {
|
|
1018
|
+
arc1Start: viewport.canvasToWorld(arc1Start),
|
|
1019
|
+
arc1End: viewport.canvasToWorld(arc1End),
|
|
1020
|
+
arc2End: viewport.canvasToWorld(arc2End),
|
|
1021
|
+
arc2Start: viewport.canvasToWorld(arc2Start),
|
|
1022
|
+
},
|
|
1023
|
+
},
|
|
875
1024
|
};
|
|
876
1025
|
}
|
|
877
1026
|
|
|
@@ -889,6 +1038,177 @@ class CobbAngleTool extends AnnotationTool {
|
|
|
889
1038
|
|
|
890
1039
|
return cachedStats;
|
|
891
1040
|
}
|
|
1041
|
+
|
|
1042
|
+
distanceToLines = ({ viewport, points, canvasCoords, proximity }) => {
|
|
1043
|
+
const [point1, point2, point3, point4] = points;
|
|
1044
|
+
const canvasPoint1 = viewport.worldToCanvas(point1);
|
|
1045
|
+
const canvasPoint2 = viewport.worldToCanvas(point2);
|
|
1046
|
+
const canvasPoint3 = viewport.worldToCanvas(point3);
|
|
1047
|
+
const canvasPoint4 = viewport.worldToCanvas(point4);
|
|
1048
|
+
|
|
1049
|
+
const line1 = {
|
|
1050
|
+
start: {
|
|
1051
|
+
x: canvasPoint1[0],
|
|
1052
|
+
y: canvasPoint1[1],
|
|
1053
|
+
},
|
|
1054
|
+
end: {
|
|
1055
|
+
x: canvasPoint2[0],
|
|
1056
|
+
y: canvasPoint2[1],
|
|
1057
|
+
},
|
|
1058
|
+
};
|
|
1059
|
+
|
|
1060
|
+
const line2 = {
|
|
1061
|
+
start: {
|
|
1062
|
+
x: canvasPoint3[0],
|
|
1063
|
+
y: canvasPoint3[1],
|
|
1064
|
+
},
|
|
1065
|
+
end: {
|
|
1066
|
+
x: canvasPoint4[0],
|
|
1067
|
+
y: canvasPoint4[1],
|
|
1068
|
+
},
|
|
1069
|
+
};
|
|
1070
|
+
|
|
1071
|
+
const distanceToPoint = lineSegment.distanceToPoint(
|
|
1072
|
+
[line1.start.x, line1.start.y],
|
|
1073
|
+
[line1.end.x, line1.end.y],
|
|
1074
|
+
[canvasCoords[0], canvasCoords[1]]
|
|
1075
|
+
);
|
|
1076
|
+
|
|
1077
|
+
const distanceToPoint2 = lineSegment.distanceToPoint(
|
|
1078
|
+
[line2.start.x, line2.start.y],
|
|
1079
|
+
[line2.end.x, line2.end.y],
|
|
1080
|
+
[canvasCoords[0], canvasCoords[1]]
|
|
1081
|
+
);
|
|
1082
|
+
|
|
1083
|
+
let isNearFirstLine = false;
|
|
1084
|
+
let isNearSecondLine = false;
|
|
1085
|
+
|
|
1086
|
+
if (distanceToPoint <= proximity) {
|
|
1087
|
+
isNearFirstLine = true;
|
|
1088
|
+
} else if (distanceToPoint2 <= proximity) {
|
|
1089
|
+
isNearSecondLine = true;
|
|
1090
|
+
}
|
|
1091
|
+
return {
|
|
1092
|
+
distanceToPoint,
|
|
1093
|
+
distanceToPoint2,
|
|
1094
|
+
isNearFirstLine,
|
|
1095
|
+
isNearSecondLine,
|
|
1096
|
+
};
|
|
1097
|
+
};
|
|
1098
|
+
|
|
1099
|
+
getArcsStartEndPoints = ({
|
|
1100
|
+
firstLine,
|
|
1101
|
+
secondLine,
|
|
1102
|
+
mid1,
|
|
1103
|
+
mid2,
|
|
1104
|
+
}): {
|
|
1105
|
+
arc1Start: Types.Point2;
|
|
1106
|
+
arc1End: Types.Point2;
|
|
1107
|
+
arc2Start: Types.Point2;
|
|
1108
|
+
arc2End: Types.Point2;
|
|
1109
|
+
arc1Angle: number;
|
|
1110
|
+
arc2Angle: number;
|
|
1111
|
+
} => {
|
|
1112
|
+
const linkLine = [mid1, mid2] as [Types.Point2, Types.Point2];
|
|
1113
|
+
|
|
1114
|
+
const arc1Angle = angleBetweenLines(firstLine, linkLine);
|
|
1115
|
+
const arc2Angle = angleBetweenLines(secondLine, linkLine);
|
|
1116
|
+
|
|
1117
|
+
const arc1Side = arc1Angle > 90 ? 1 : 0;
|
|
1118
|
+
const arc2Side = arc2Angle > 90 ? 0 : 1;
|
|
1119
|
+
|
|
1120
|
+
const midLinkLine = midPoint2(linkLine[0], linkLine[1]);
|
|
1121
|
+
|
|
1122
|
+
const linkLineLength = Math.sqrt(
|
|
1123
|
+
(linkLine[1][0] - linkLine[0][0]) ** 2 +
|
|
1124
|
+
(linkLine[1][1] - linkLine[0][1]) ** 2
|
|
1125
|
+
);
|
|
1126
|
+
const ratio = 0.1; // 10% of the line length
|
|
1127
|
+
|
|
1128
|
+
const midFirstLine = midPoint2(firstLine[0], firstLine[1]);
|
|
1129
|
+
const midSecondLine = midPoint2(secondLine[0], secondLine[1]);
|
|
1130
|
+
|
|
1131
|
+
// For arc1Start
|
|
1132
|
+
const directionVectorStartArc1 = [
|
|
1133
|
+
firstLine[arc1Side][0] - midFirstLine[0],
|
|
1134
|
+
firstLine[arc1Side][1] - midFirstLine[1],
|
|
1135
|
+
];
|
|
1136
|
+
const magnitudeStartArc1 = Math.sqrt(
|
|
1137
|
+
directionVectorStartArc1[0] ** 2 + directionVectorStartArc1[1] ** 2
|
|
1138
|
+
);
|
|
1139
|
+
const normalizedDirectionStartArc1 = [
|
|
1140
|
+
directionVectorStartArc1[0] / magnitudeStartArc1,
|
|
1141
|
+
directionVectorStartArc1[1] / magnitudeStartArc1,
|
|
1142
|
+
];
|
|
1143
|
+
const arc1Start = [
|
|
1144
|
+
midFirstLine[0] +
|
|
1145
|
+
normalizedDirectionStartArc1[0] * linkLineLength * ratio,
|
|
1146
|
+
midFirstLine[1] +
|
|
1147
|
+
normalizedDirectionStartArc1[1] * linkLineLength * ratio,
|
|
1148
|
+
] as Types.Point2;
|
|
1149
|
+
|
|
1150
|
+
// Existing logic for arc1End
|
|
1151
|
+
const directionVectorEndArc1 = [
|
|
1152
|
+
midLinkLine[0] - mid1[0],
|
|
1153
|
+
midLinkLine[1] - mid1[1],
|
|
1154
|
+
];
|
|
1155
|
+
const magnitudeEndArc1 = Math.sqrt(
|
|
1156
|
+
directionVectorEndArc1[0] ** 2 + directionVectorEndArc1[1] ** 2
|
|
1157
|
+
);
|
|
1158
|
+
const normalizedDirectionEndArc1 = [
|
|
1159
|
+
directionVectorEndArc1[0] / magnitudeEndArc1,
|
|
1160
|
+
directionVectorEndArc1[1] / magnitudeEndArc1,
|
|
1161
|
+
];
|
|
1162
|
+
const arc1End = [
|
|
1163
|
+
mid1[0] + normalizedDirectionEndArc1[0] * linkLineLength * ratio,
|
|
1164
|
+
mid1[1] + normalizedDirectionEndArc1[1] * linkLineLength * ratio,
|
|
1165
|
+
] as Types.Point2;
|
|
1166
|
+
|
|
1167
|
+
// Similar logic for arc2Start
|
|
1168
|
+
const directionVectorStartArc2 = [
|
|
1169
|
+
secondLine[arc2Side][0] - midSecondLine[0],
|
|
1170
|
+
secondLine[arc2Side][1] - midSecondLine[1],
|
|
1171
|
+
];
|
|
1172
|
+
const magnitudeStartArc2 = Math.sqrt(
|
|
1173
|
+
directionVectorStartArc2[0] ** 2 + directionVectorStartArc2[1] ** 2
|
|
1174
|
+
);
|
|
1175
|
+
const normalizedDirectionStartArc2 = [
|
|
1176
|
+
directionVectorStartArc2[0] / magnitudeStartArc2,
|
|
1177
|
+
directionVectorStartArc2[1] / magnitudeStartArc2,
|
|
1178
|
+
];
|
|
1179
|
+
const arc2Start = [
|
|
1180
|
+
midSecondLine[0] +
|
|
1181
|
+
normalizedDirectionStartArc2[0] * linkLineLength * ratio,
|
|
1182
|
+
midSecondLine[1] +
|
|
1183
|
+
normalizedDirectionStartArc2[1] * linkLineLength * ratio,
|
|
1184
|
+
] as Types.Point2;
|
|
1185
|
+
|
|
1186
|
+
// Similar logic for arc2End
|
|
1187
|
+
const directionVectorEndArc2 = [
|
|
1188
|
+
midLinkLine[0] - mid2[0],
|
|
1189
|
+
midLinkLine[1] - mid2[1],
|
|
1190
|
+
];
|
|
1191
|
+
const magnitudeEndArc2 = Math.sqrt(
|
|
1192
|
+
directionVectorEndArc2[0] ** 2 + directionVectorEndArc2[1] ** 2
|
|
1193
|
+
);
|
|
1194
|
+
const normalizedDirectionEndArc2 = [
|
|
1195
|
+
directionVectorEndArc2[0] / magnitudeEndArc2,
|
|
1196
|
+
directionVectorEndArc2[1] / magnitudeEndArc2,
|
|
1197
|
+
];
|
|
1198
|
+
const arc2End = [
|
|
1199
|
+
mid2[0] + normalizedDirectionEndArc2[0] * linkLineLength * ratio,
|
|
1200
|
+
mid2[1] + normalizedDirectionEndArc2[1] * linkLineLength * ratio,
|
|
1201
|
+
] as Types.Point2;
|
|
1202
|
+
|
|
1203
|
+
return {
|
|
1204
|
+
arc1Start,
|
|
1205
|
+
arc1End,
|
|
1206
|
+
arc2Start,
|
|
1207
|
+
arc2End,
|
|
1208
|
+
arc1Angle: arc1Angle > 90 ? 180 - arc1Angle : arc1Angle,
|
|
1209
|
+
arc2Angle: arc2Angle > 90 ? 180 - arc2Angle : arc2Angle,
|
|
1210
|
+
};
|
|
1211
|
+
};
|
|
892
1212
|
}
|
|
893
1213
|
|
|
894
1214
|
function defaultGetTextLines(data, targetId): string[] {
|