@teachinglab/omd 0.7.10 → 0.7.11
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 +76 -36
- package/package.json +1 -1
|
@@ -663,11 +663,11 @@ export class SelectTool extends Tool {
|
|
|
663
663
|
|
|
664
664
|
/**
|
|
665
665
|
* Gets the bounds of an OMD element including transform
|
|
666
|
+
* Uses clip paths for accurate bounds when present (e.g., coordinate planes)
|
|
666
667
|
* @private
|
|
667
668
|
*/
|
|
668
669
|
_getOMDElementBounds(item) {
|
|
669
670
|
try {
|
|
670
|
-
const bbox = item.getBBox();
|
|
671
671
|
const transform = item.getAttribute('transform') || '';
|
|
672
672
|
let offsetX = 0, offsetY = 0, scaleX = 1, scaleY = 1;
|
|
673
673
|
|
|
@@ -683,6 +683,68 @@ export class SelectTool extends Tool {
|
|
|
683
683
|
scaleY = scaleMatch[2] ? parseFloat(scaleMatch[2]) : scaleX;
|
|
684
684
|
}
|
|
685
685
|
|
|
686
|
+
// Get the content element (usually an SVG inside the wrapper)
|
|
687
|
+
const content = item.firstElementChild;
|
|
688
|
+
let bbox;
|
|
689
|
+
|
|
690
|
+
if (content) {
|
|
691
|
+
// Check for clip paths to get accurate visible bounds
|
|
692
|
+
// This is important for coordinate planes where graph lines extend beyond visible area
|
|
693
|
+
const clipPaths = content.querySelectorAll('clipPath');
|
|
694
|
+
|
|
695
|
+
if (clipPaths.length > 0) {
|
|
696
|
+
let maxArea = 0;
|
|
697
|
+
let clipBounds = null;
|
|
698
|
+
|
|
699
|
+
for (const clipPath of clipPaths) {
|
|
700
|
+
const rect = clipPath.querySelector('rect');
|
|
701
|
+
if (rect) {
|
|
702
|
+
const w = parseFloat(rect.getAttribute('width')) || 0;
|
|
703
|
+
const h = parseFloat(rect.getAttribute('height')) || 0;
|
|
704
|
+
const x = parseFloat(rect.getAttribute('x')) || 0;
|
|
705
|
+
const y = parseFloat(rect.getAttribute('y')) || 0;
|
|
706
|
+
|
|
707
|
+
const area = w * h;
|
|
708
|
+
if (area > maxArea) {
|
|
709
|
+
maxArea = area;
|
|
710
|
+
|
|
711
|
+
// Find the transform on the content group
|
|
712
|
+
const contentGroup = content.firstElementChild;
|
|
713
|
+
let tx = 0, ty = 0;
|
|
714
|
+
if (contentGroup) {
|
|
715
|
+
const contentTransform = contentGroup.getAttribute('transform');
|
|
716
|
+
if (contentTransform) {
|
|
717
|
+
const contentTranslateMatch = contentTransform.match(/translate\(\s*([^,]+)(?:,\s*([^)]+))?\s*\)/);
|
|
718
|
+
if (contentTranslateMatch) {
|
|
719
|
+
tx = parseFloat(contentTranslateMatch[1]) || 0;
|
|
720
|
+
ty = parseFloat(contentTranslateMatch[2]) || 0;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
clipBounds = {
|
|
726
|
+
x: x + tx,
|
|
727
|
+
y: y + ty,
|
|
728
|
+
width: w,
|
|
729
|
+
height: h
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
if (clipBounds) {
|
|
736
|
+
bbox = clipBounds;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// Fallback to getBBox if no clip path found
|
|
741
|
+
if (!bbox) {
|
|
742
|
+
bbox = content.getBBox();
|
|
743
|
+
}
|
|
744
|
+
} else {
|
|
745
|
+
bbox = item.getBBox();
|
|
746
|
+
}
|
|
747
|
+
|
|
686
748
|
return {
|
|
687
749
|
x: offsetX + (bbox.x * scaleX),
|
|
688
750
|
y: offsetY + (bbox.y * scaleY),
|
|
@@ -738,42 +800,20 @@ export class SelectTool extends Tool {
|
|
|
738
800
|
continue;
|
|
739
801
|
}
|
|
740
802
|
try {
|
|
741
|
-
//
|
|
742
|
-
const
|
|
743
|
-
|
|
744
|
-
// Parse transform to get the actual position
|
|
745
|
-
const transform = item.getAttribute('transform') || '';
|
|
746
|
-
let offsetX = 0, offsetY = 0, scaleX = 1, scaleY = 1;
|
|
747
|
-
|
|
748
|
-
// Parse translate
|
|
749
|
-
const translateMatch = transform.match(/translate\(\s*([^,]+)\s*,\s*([^)]+)\s*\)/);
|
|
750
|
-
if (translateMatch) {
|
|
751
|
-
offsetX = parseFloat(translateMatch[1]) || 0;
|
|
752
|
-
offsetY = parseFloat(translateMatch[2]) || 0;
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
// Parse scale
|
|
756
|
-
const scaleMatch = transform.match(/scale\(\s*([^,)]+)(?:\s*,\s*([^)]+))?\s*\)/);
|
|
757
|
-
if (scaleMatch) {
|
|
758
|
-
scaleX = parseFloat(scaleMatch[1]) || 1;
|
|
759
|
-
scaleY = scaleMatch[2] ? parseFloat(scaleMatch[2]) : scaleX;
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
// Calculate the actual bounds including transform
|
|
763
|
-
const actualX = offsetX + (bbox.x * scaleX);
|
|
764
|
-
const actualY = offsetY + (bbox.y * scaleY);
|
|
765
|
-
const actualWidth = bbox.width * scaleX;
|
|
766
|
-
const actualHeight = bbox.height * scaleY;
|
|
767
|
-
|
|
768
|
-
// Add some padding for easier clicking
|
|
769
|
-
const padding = 10;
|
|
803
|
+
// Use the shared bounds calculation method (handles clip paths correctly)
|
|
804
|
+
const itemBounds = this._getOMDElementBounds(item);
|
|
770
805
|
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
806
|
+
if (itemBounds) {
|
|
807
|
+
// Add some padding for easier clicking
|
|
808
|
+
const padding = 10;
|
|
809
|
+
|
|
810
|
+
// Check if point is within the bounds (with padding)
|
|
811
|
+
if (x >= itemBounds.x - padding &&
|
|
812
|
+
x <= itemBounds.x + itemBounds.width + padding &&
|
|
813
|
+
y >= itemBounds.y - padding &&
|
|
814
|
+
y <= itemBounds.y + itemBounds.height + padding) {
|
|
815
|
+
return item;
|
|
816
|
+
}
|
|
777
817
|
}
|
|
778
818
|
} catch (error) {
|
|
779
819
|
// Skip items that can't be measured (continue with next)
|