@svgedit/svgcanvas 7.2.1 → 7.2.2
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/CHANGES.md +8 -0
- package/core/coords.js +3 -3
- package/core/elem-get-set.js +80 -90
- package/core/event.js +5 -5
- package/core/math.js +90 -52
- package/core/path-actions.js +2 -2
- package/core/path.js +2 -2
- package/core/recalculate.js +8 -9
- package/core/select.js +3 -3
- package/core/selected-elem.js +6 -5
- package/core/selection.js +3 -2
- package/core/svg-exec.js +29 -39
- package/core/undo.js +5 -5
- package/core/utilities.js +449 -354
- package/dist/svgcanvas.js +4925 -6334
- package/dist/svgcanvas.js.map +1 -1
- package/package.json +1 -1
- package/publish.md +6 -0
- package/rollup.config.mjs +4 -9
- package/dist/svgcanvas.esm.js +0 -55680
- package/dist/svgcanvas.esm.js.map +0 -1
package/CHANGES.md
ADDED
package/core/coords.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
snapToGrid, assignAttributes, getBBox, getRefElem, findDefs
|
|
9
9
|
} from './utilities.js'
|
|
10
10
|
import {
|
|
11
|
-
transformPoint, transformListToTransform, matrixMultiply, transformBox
|
|
11
|
+
transformPoint, transformListToTransform, matrixMultiply, transformBox, getTransformList
|
|
12
12
|
} from './math.js'
|
|
13
13
|
|
|
14
14
|
// this is how we map paths to our preferred relative segment types
|
|
@@ -98,7 +98,7 @@ export const remapElement = (selected, changes, m) => {
|
|
|
98
98
|
changes.y = Number.parseFloat(changes.y) + tNew.f
|
|
99
99
|
} else {
|
|
100
100
|
// we just absorb all matrices into the element and don't do any remapping
|
|
101
|
-
const chlist = selected
|
|
101
|
+
const chlist = getTransformList(selected)
|
|
102
102
|
const mt = svgCanvas.getSvgRoot().createSVGTransform()
|
|
103
103
|
mt.setMatrix(matrixMultiply(transformListToTransform(chlist).matrix, m))
|
|
104
104
|
chlist.clear()
|
|
@@ -115,7 +115,7 @@ export const remapElement = (selected, changes, m) => {
|
|
|
115
115
|
// Allow images to be inverted (give them matrix when flipped)
|
|
116
116
|
if (elName === 'image' && (m.a < 0 || m.d < 0)) {
|
|
117
117
|
// Convert to matrix
|
|
118
|
-
const chlist = selected
|
|
118
|
+
const chlist = getTransformList(selected)
|
|
119
119
|
const mt = svgCanvas.getSvgRoot().createSVGTransform()
|
|
120
120
|
mt.setMatrix(matrixMultiply(transformListToTransform(chlist).matrix, m))
|
|
121
121
|
chlist.clear()
|
package/core/elem-get-set.js
CHANGED
|
@@ -526,6 +526,7 @@ const setPaintMethod = (type, paint) => {
|
|
|
526
526
|
break
|
|
527
527
|
}
|
|
528
528
|
}
|
|
529
|
+
|
|
529
530
|
/**
|
|
530
531
|
* Sets the stroke width for the current selected elements.
|
|
531
532
|
* When attempting to set a line's width to 0, this changes it to 1 instead.
|
|
@@ -599,54 +600,41 @@ const setStrokeAttrMethod = (attr, val) => {
|
|
|
599
600
|
svgCanvas.call('changed', selectedElements)
|
|
600
601
|
}
|
|
601
602
|
}
|
|
603
|
+
|
|
602
604
|
/**
|
|
603
|
-
* Check
|
|
604
|
-
* @function module:svgcanvas.SvgCanvas#getBold
|
|
605
|
-
* @returns {boolean}
|
|
606
|
-
*/
|
|
605
|
+
* Check if all selected text elements are in bold.
|
|
606
|
+
* @function module:svgcanvas.SvgCanvas#getBold
|
|
607
|
+
* @returns {boolean} `true` if all selected elements are bold, `false` otherwise.
|
|
608
|
+
*/
|
|
607
609
|
const getBoldMethod = () => {
|
|
608
610
|
const selectedElements = svgCanvas.getSelectedElements()
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
if (selected?.tagName === 'text' &&
|
|
612
|
-
!selectedElements[1]) {
|
|
613
|
-
return (selected.getAttribute('font-weight') === 'bold')
|
|
614
|
-
}
|
|
615
|
-
return false
|
|
611
|
+
const textElements = selectedElements.filter(el => el?.tagName === 'text')
|
|
612
|
+
return textElements.every(el => el.getAttribute('font-weight') === 'bold')
|
|
616
613
|
}
|
|
617
614
|
|
|
618
615
|
/**
|
|
619
|
-
* Make the selected element bold or normal.
|
|
620
|
-
* @function module:svgcanvas.SvgCanvas#setBold
|
|
621
|
-
* @param {boolean} b - Indicates bold (`true`) or normal (`false`)
|
|
622
|
-
* @returns {void}
|
|
623
|
-
*/
|
|
616
|
+
* Make the selected element(s) bold or normal.
|
|
617
|
+
* @function module:svgcanvas.SvgCanvas#setBold
|
|
618
|
+
* @param {boolean} b - Indicates bold (`true`) or normal (`false`)
|
|
619
|
+
* @returns {void}
|
|
620
|
+
*/
|
|
624
621
|
const setBoldMethod = (b) => {
|
|
625
622
|
const selectedElements = svgCanvas.getSelectedElements()
|
|
626
|
-
const
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
svgCanvas.changeSelectedAttribute('font-weight', b ? 'bold' : 'normal')
|
|
630
|
-
}
|
|
631
|
-
if (!selectedElements[0].textContent) {
|
|
623
|
+
const textElements = selectedElements.filter(el => el?.tagName === 'text')
|
|
624
|
+
svgCanvas.changeSelectedAttribute('font-weight', b ? 'bold' : 'normal', textElements)
|
|
625
|
+
if (!textElements.some(el => el.textContent)) {
|
|
632
626
|
svgCanvas.textActions.setCursor()
|
|
633
627
|
}
|
|
634
628
|
}
|
|
635
629
|
|
|
636
630
|
/**
|
|
637
|
-
* Check
|
|
638
|
-
* @returns {boolean} Indicates whether or not
|
|
631
|
+
* Check if all selected text elements have the given text decoration value or not.
|
|
632
|
+
* @returns {boolean} Indicates whether or not elements have the text decoration value
|
|
639
633
|
*/
|
|
640
634
|
const hasTextDecorationMethod = (value) => {
|
|
641
635
|
const selectedElements = svgCanvas.getSelectedElements()
|
|
642
|
-
const
|
|
643
|
-
|
|
644
|
-
if (selected?.tagName === 'text' && !selectedElements[1]) {
|
|
645
|
-
const attribute = selected.getAttribute('text-decoration') || ''
|
|
646
|
-
return attribute.includes(value)
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
return false
|
|
636
|
+
const textElements = selectedElements.filter(el => el?.tagName === 'text')
|
|
637
|
+
return textElements.every(el => (el.getAttribute('text-decoration') || '').includes(value))
|
|
650
638
|
}
|
|
651
639
|
|
|
652
640
|
/**
|
|
@@ -655,13 +643,24 @@ const hasTextDecorationMethod = (value) => {
|
|
|
655
643
|
* @returns {void}
|
|
656
644
|
*/
|
|
657
645
|
const addTextDecorationMethod = (value) => {
|
|
646
|
+
const { ChangeElementCommand, BatchCommand } = svgCanvas.history
|
|
658
647
|
const selectedElements = svgCanvas.getSelectedElements()
|
|
659
|
-
const
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
648
|
+
const textElements = selectedElements.filter(el => el?.tagName === 'text')
|
|
649
|
+
|
|
650
|
+
const batchCmd = new BatchCommand()
|
|
651
|
+
textElements.forEach(elem => {
|
|
652
|
+
const oldValue = elem.getAttribute('text-decoration') || ''
|
|
653
|
+
// Add the new text decoration value if it did not exist
|
|
654
|
+
if (!oldValue.includes(value)) {
|
|
655
|
+
batchCmd.addSubCommand(new ChangeElementCommand(elem, { 'text-decoration': oldValue }))
|
|
656
|
+
svgCanvas.changeSelectedAttributeNoUndo('text-decoration', (oldValue + ' ' + value).trim(), [elem])
|
|
657
|
+
}
|
|
658
|
+
})
|
|
659
|
+
if (!batchCmd.isEmpty()) {
|
|
660
|
+
svgCanvas.undoMgr.addCommandToHistory(batchCmd)
|
|
663
661
|
}
|
|
664
|
-
|
|
662
|
+
|
|
663
|
+
if (!textElements.some(el => el.textContent)) {
|
|
665
664
|
svgCanvas.textActions.setCursor()
|
|
666
665
|
}
|
|
667
666
|
}
|
|
@@ -672,44 +671,47 @@ const addTextDecorationMethod = (value) => {
|
|
|
672
671
|
* @returns {void}
|
|
673
672
|
*/
|
|
674
673
|
const removeTextDecorationMethod = (value) => {
|
|
674
|
+
const { ChangeElementCommand, BatchCommand } = svgCanvas.history
|
|
675
675
|
const selectedElements = svgCanvas.getSelectedElements()
|
|
676
|
-
const
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
676
|
+
const textElements = selectedElements.filter(el => el?.tagName === 'text')
|
|
677
|
+
|
|
678
|
+
const batchCmd = new BatchCommand()
|
|
679
|
+
textElements.forEach(elem => {
|
|
680
|
+
const actualValues = elem.getAttribute('text-decoration') || ''
|
|
681
|
+
batchCmd.addSubCommand(new ChangeElementCommand(elem, { 'text-decoration': actualValues }))
|
|
682
|
+
svgCanvas.changeSelectedAttributeNoUndo('text-decoration', actualValues.replace(value, '').trim(), [elem])
|
|
683
|
+
})
|
|
684
|
+
if (!batchCmd.isEmpty()) {
|
|
685
|
+
svgCanvas.undoMgr.addCommandToHistory(batchCmd)
|
|
680
686
|
}
|
|
681
|
-
|
|
687
|
+
|
|
688
|
+
if (!textElements.some(el => el.textContent)) {
|
|
682
689
|
svgCanvas.textActions.setCursor()
|
|
683
690
|
}
|
|
684
691
|
}
|
|
685
692
|
|
|
686
693
|
/**
|
|
687
|
-
* Check
|
|
688
|
-
* @function module:svgcanvas.SvgCanvas#getItalic
|
|
689
|
-
* @returns {boolean}
|
|
690
|
-
*/
|
|
694
|
+
* Check if all selected elements have an italic font style.
|
|
695
|
+
* @function module:svgcanvas.SvgCanvas#getItalic
|
|
696
|
+
* @returns {boolean} `true` if all selected elements are in italics, `false` otherwise.
|
|
697
|
+
*/
|
|
691
698
|
const getItalicMethod = () => {
|
|
692
699
|
const selectedElements = svgCanvas.getSelectedElements()
|
|
693
|
-
const
|
|
694
|
-
|
|
695
|
-
return (selected.getAttribute('font-style') === 'italic')
|
|
696
|
-
}
|
|
697
|
-
return false
|
|
700
|
+
const textElements = selectedElements.filter(el => el?.tagName === 'text')
|
|
701
|
+
return textElements.every(el => el.getAttribute('font-style') === 'italic')
|
|
698
702
|
}
|
|
699
703
|
|
|
700
704
|
/**
|
|
701
|
-
* Make the selected element italic or normal.
|
|
702
|
-
* @function module:svgcanvas.SvgCanvas#setItalic
|
|
703
|
-
* @param {boolean} i - Indicates italic (`true`) or normal (`false`)
|
|
704
|
-
* @returns {void}
|
|
705
|
-
*/
|
|
705
|
+
* Make the selected element(s) italic or normal.
|
|
706
|
+
* @function module:svgcanvas.SvgCanvas#setItalic
|
|
707
|
+
* @param {boolean} i - Indicates italic (`true`) or normal (`false`)
|
|
708
|
+
* @returns {void}
|
|
709
|
+
*/
|
|
706
710
|
const setItalicMethod = (i) => {
|
|
707
711
|
const selectedElements = svgCanvas.getSelectedElements()
|
|
708
|
-
const
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
}
|
|
712
|
-
if (!selectedElements[0].textContent) {
|
|
712
|
+
const textElements = selectedElements.filter(el => el?.tagName === 'text')
|
|
713
|
+
svgCanvas.changeSelectedAttribute('font-style', i ? 'italic' : 'normal', textElements)
|
|
714
|
+
if (!textElements.some(el => el.textContent)) {
|
|
713
715
|
svgCanvas.textActions.setCursor()
|
|
714
716
|
}
|
|
715
717
|
}
|
|
@@ -721,13 +723,8 @@ const setItalicMethod = (i) => {
|
|
|
721
723
|
*/
|
|
722
724
|
const setTextAnchorMethod = (value) => {
|
|
723
725
|
const selectedElements = svgCanvas.getSelectedElements()
|
|
724
|
-
const
|
|
725
|
-
|
|
726
|
-
svgCanvas.changeSelectedAttribute('text-anchor', value)
|
|
727
|
-
}
|
|
728
|
-
if (selectedElements.length > 0 && !selectedElements[0].textContent) {
|
|
729
|
-
svgCanvas.textActions.setCursor()
|
|
730
|
-
}
|
|
726
|
+
const textElements = selectedElements.filter(el => el?.tagName === 'text')
|
|
727
|
+
svgCanvas.changeSelectedAttribute('text-anchor', value, textElements)
|
|
731
728
|
}
|
|
732
729
|
|
|
733
730
|
/**
|
|
@@ -737,11 +734,9 @@ const setTextAnchorMethod = (value) => {
|
|
|
737
734
|
*/
|
|
738
735
|
const setLetterSpacingMethod = (value) => {
|
|
739
736
|
const selectedElements = svgCanvas.getSelectedElements()
|
|
740
|
-
const
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
}
|
|
744
|
-
if (selectedElements.length > 0 && !selectedElements[0].textContent) {
|
|
737
|
+
const textElements = selectedElements.filter(el => el?.tagName === 'text')
|
|
738
|
+
svgCanvas.changeSelectedAttribute('letter-spacing', value, textElements)
|
|
739
|
+
if (!textElements.some(el => el.textContent)) {
|
|
745
740
|
svgCanvas.textActions.setCursor()
|
|
746
741
|
}
|
|
747
742
|
}
|
|
@@ -753,11 +748,9 @@ const setLetterSpacingMethod = (value) => {
|
|
|
753
748
|
*/
|
|
754
749
|
const setWordSpacingMethod = (value) => {
|
|
755
750
|
const selectedElements = svgCanvas.getSelectedElements()
|
|
756
|
-
const
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
}
|
|
760
|
-
if (selectedElements.length > 0 && !selectedElements[0].textContent) {
|
|
751
|
+
const textElements = selectedElements.filter(el => el?.tagName === 'text')
|
|
752
|
+
svgCanvas.changeSelectedAttribute('word-spacing', value, textElements)
|
|
753
|
+
if (!textElements.some(el => el.textContent)) {
|
|
761
754
|
svgCanvas.textActions.setCursor()
|
|
762
755
|
}
|
|
763
756
|
}
|
|
@@ -769,11 +762,9 @@ const setWordSpacingMethod = (value) => {
|
|
|
769
762
|
*/
|
|
770
763
|
const setTextLengthMethod = (value) => {
|
|
771
764
|
const selectedElements = svgCanvas.getSelectedElements()
|
|
772
|
-
const
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
}
|
|
776
|
-
if (selectedElements.length > 0 && !selectedElements[0].textContent) {
|
|
765
|
+
const textElements = selectedElements.filter(el => el?.tagName === 'text')
|
|
766
|
+
svgCanvas.changeSelectedAttribute('textLength', value, textElements)
|
|
767
|
+
if (!textElements.some(el => el.textContent)) {
|
|
777
768
|
svgCanvas.textActions.setCursor()
|
|
778
769
|
}
|
|
779
770
|
}
|
|
@@ -785,11 +776,9 @@ const setTextLengthMethod = (value) => {
|
|
|
785
776
|
*/
|
|
786
777
|
const setLengthAdjustMethod = (value) => {
|
|
787
778
|
const selectedElements = svgCanvas.getSelectedElements()
|
|
788
|
-
const
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
}
|
|
792
|
-
if (selectedElements.length > 0 && !selectedElements[0].textContent) {
|
|
779
|
+
const textElements = selectedElements.filter(el => el?.tagName === 'text')
|
|
780
|
+
svgCanvas.changeSelectedAttribute('lengthAdjust', value, textElements)
|
|
781
|
+
if (!textElements.some(el => el.textContent)) {
|
|
793
782
|
svgCanvas.textActions.setCursor()
|
|
794
783
|
}
|
|
795
784
|
}
|
|
@@ -810,9 +799,10 @@ const getFontFamilyMethod = () => {
|
|
|
810
799
|
*/
|
|
811
800
|
const setFontFamilyMethod = (val) => {
|
|
812
801
|
const selectedElements = svgCanvas.getSelectedElements()
|
|
802
|
+
const textElements = selectedElements.filter(el => el?.tagName === 'text')
|
|
813
803
|
svgCanvas.setCurText('font_family', val)
|
|
814
|
-
svgCanvas.changeSelectedAttribute('font-family', val)
|
|
815
|
-
if (!
|
|
804
|
+
svgCanvas.changeSelectedAttribute('font-family', val, textElements)
|
|
805
|
+
if (!textElements.some(el => el.textContent)) {
|
|
816
806
|
svgCanvas.textActions.setCursor()
|
|
817
807
|
}
|
|
818
808
|
}
|
package/core/event.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
convertAttrs
|
|
13
13
|
} from './units.js'
|
|
14
14
|
import {
|
|
15
|
-
transformPoint, hasMatrixTransform, getMatrix, snapToAngle
|
|
15
|
+
transformPoint, hasMatrixTransform, getMatrix, snapToAngle, getTransformList
|
|
16
16
|
} from './math.js'
|
|
17
17
|
import * as draw from './draw.js'
|
|
18
18
|
import * as pathModule from './path.js'
|
|
@@ -83,7 +83,7 @@ const getBsplinePoint = (t) => {
|
|
|
83
83
|
const updateTransformList = (svgRoot, element, dx, dy) => {
|
|
84
84
|
const xform = svgRoot.createSVGTransform()
|
|
85
85
|
xform.setTranslate(dx, dy)
|
|
86
|
-
const tlist = element
|
|
86
|
+
const tlist = getTransformList(element)
|
|
87
87
|
if (tlist.numberOfItems) {
|
|
88
88
|
const firstItem = tlist.getItem(0)
|
|
89
89
|
if (firstItem.type === 2) { // SVG_TRANSFORM_TRANSLATE = 2
|
|
@@ -221,7 +221,7 @@ const mouseMoveEvent = (evt) => {
|
|
|
221
221
|
// we track the resize bounding box and translate/scale the selected element
|
|
222
222
|
// while the mouse is down, when mouse goes up, we use this to recalculate
|
|
223
223
|
// the shape's coordinates
|
|
224
|
-
tlist = selected
|
|
224
|
+
tlist = getTransformList(selected)
|
|
225
225
|
const hasMatrix = hasMatrixTransform(tlist)
|
|
226
226
|
box = hasMatrix ? svgCanvas.getInitBbox() : getBBox(selected)
|
|
227
227
|
let left = box.x
|
|
@@ -1026,7 +1026,7 @@ const mouseDownEvent = (evt) => {
|
|
|
1026
1026
|
|
|
1027
1027
|
svgCanvas.setStartTransform(mouseTarget.getAttribute('transform'))
|
|
1028
1028
|
|
|
1029
|
-
const tlist = mouseTarget
|
|
1029
|
+
const tlist = getTransformList(mouseTarget)
|
|
1030
1030
|
// consolidate transforms using standard SVG but keep the transformation used for the move/scale
|
|
1031
1031
|
if (tlist.numberOfItems > 1) {
|
|
1032
1032
|
const firstTransform = tlist.getItem(0)
|
|
@@ -1060,7 +1060,7 @@ const mouseDownEvent = (evt) => {
|
|
|
1060
1060
|
// a transform to use for its translate
|
|
1061
1061
|
for (const selectedElement of selectedElements) {
|
|
1062
1062
|
if (!selectedElement) { continue }
|
|
1063
|
-
const slist = selectedElement
|
|
1063
|
+
const slist = getTransformList(selectedElement)
|
|
1064
1064
|
if (slist.numberOfItems) {
|
|
1065
1065
|
slist.insertItemBefore(svgRoot.createSVGTransform(), 0)
|
|
1066
1066
|
} else {
|
package/core/math.js
CHANGED
|
@@ -7,17 +7,17 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* @typedef {PlainObject} module:math.AngleCoord45
|
|
11
|
-
* @property {Float} x - The angle-snapped x value
|
|
12
|
-
* @property {Float} y - The angle-snapped y value
|
|
13
|
-
* @property {Integer} a - The angle at which to snap
|
|
14
|
-
*/
|
|
10
|
+
* @typedef {PlainObject} module:math.AngleCoord45
|
|
11
|
+
* @property {Float} x - The angle-snapped x value
|
|
12
|
+
* @property {Float} y - The angle-snapped y value
|
|
13
|
+
* @property {Integer} a - The angle at which to snap
|
|
14
|
+
*/
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* @typedef {PlainObject} module:math.XYObject
|
|
18
|
-
* @property {Float} x
|
|
19
|
-
* @property {Float} y
|
|
20
|
-
*/
|
|
17
|
+
* @typedef {PlainObject} module:math.XYObject
|
|
18
|
+
* @property {Float} x
|
|
19
|
+
* @property {Float} y
|
|
20
|
+
*/
|
|
21
21
|
|
|
22
22
|
import { NS } from './namespaces.js'
|
|
23
23
|
|
|
@@ -35,20 +35,35 @@ const svg = document.createElementNS(NS.SVG, 'svg')
|
|
|
35
35
|
* @param {Float} y - Float representing the y coordinate
|
|
36
36
|
* @param {SVGMatrix} m - Matrix object to transform the point with
|
|
37
37
|
* @returns {module:math.XYObject} An x, y object representing the transformed point
|
|
38
|
-
*/
|
|
38
|
+
*/
|
|
39
39
|
export const transformPoint = function (x, y, m) {
|
|
40
40
|
return { x: m.a * x + m.c * y + m.e, y: m.b * x + m.d * y + m.f }
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
export const getTransformList = (elem) => {
|
|
44
|
+
if (elem.transform) {
|
|
45
|
+
return elem.transform?.baseVal
|
|
46
|
+
}
|
|
47
|
+
if (elem.gradientTransform) {
|
|
48
|
+
return elem.gradientTransform?.baseVal
|
|
49
|
+
}
|
|
50
|
+
if (elem.patternTransform) {
|
|
51
|
+
return elem.patternTransform?.baseVal
|
|
52
|
+
}
|
|
53
|
+
console.warn('no transform list found - check browser version', elem)
|
|
54
|
+
}
|
|
55
|
+
|
|
43
56
|
/**
|
|
44
57
|
* Helper function to check if the matrix performs no actual transform
|
|
45
58
|
* (i.e. exists for identity purposes).
|
|
46
59
|
* @function module:math.isIdentity
|
|
47
60
|
* @param {SVGMatrix} m - The matrix object to check
|
|
48
61
|
* @returns {boolean} Indicates whether or not the matrix is 1,0,0,1,0,0
|
|
49
|
-
*/
|
|
62
|
+
*/
|
|
50
63
|
export const isIdentity = function (m) {
|
|
51
|
-
return (
|
|
64
|
+
return (
|
|
65
|
+
m.a === 1 && m.b === 0 && m.c === 0 && m.d === 1 && m.e === 0 && m.f === 0
|
|
66
|
+
)
|
|
52
67
|
}
|
|
53
68
|
|
|
54
69
|
/**
|
|
@@ -57,18 +72,30 @@ export const isIdentity = function (m) {
|
|
|
57
72
|
* @function module:math.matrixMultiply
|
|
58
73
|
* @param {...SVGMatrix} args - Matrix objects to multiply
|
|
59
74
|
* @returns {SVGMatrix} The matrix object resulting from the calculation
|
|
60
|
-
*/
|
|
75
|
+
*/
|
|
61
76
|
export const matrixMultiply = function (...args) {
|
|
62
77
|
const m = args.reduceRight((prev, m1) => {
|
|
63
78
|
return m1.multiply(prev)
|
|
64
79
|
})
|
|
65
80
|
|
|
66
|
-
if (Math.abs(m.a) < NEAR_ZERO) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (Math.abs(m.
|
|
70
|
-
|
|
71
|
-
|
|
81
|
+
if (Math.abs(m.a) < NEAR_ZERO) {
|
|
82
|
+
m.a = 0
|
|
83
|
+
}
|
|
84
|
+
if (Math.abs(m.b) < NEAR_ZERO) {
|
|
85
|
+
m.b = 0
|
|
86
|
+
}
|
|
87
|
+
if (Math.abs(m.c) < NEAR_ZERO) {
|
|
88
|
+
m.c = 0
|
|
89
|
+
}
|
|
90
|
+
if (Math.abs(m.d) < NEAR_ZERO) {
|
|
91
|
+
m.d = 0
|
|
92
|
+
}
|
|
93
|
+
if (Math.abs(m.e) < NEAR_ZERO) {
|
|
94
|
+
m.e = 0
|
|
95
|
+
}
|
|
96
|
+
if (Math.abs(m.f) < NEAR_ZERO) {
|
|
97
|
+
m.f = 0
|
|
98
|
+
}
|
|
72
99
|
|
|
73
100
|
return m
|
|
74
101
|
}
|
|
@@ -78,29 +105,33 @@ export const matrixMultiply = function (...args) {
|
|
|
78
105
|
* @function module:math.hasMatrixTransform
|
|
79
106
|
* @param {SVGTransformList} [tlist] - The transformlist to check
|
|
80
107
|
* @returns {boolean} Whether or not a matrix transform was found
|
|
81
|
-
*/
|
|
108
|
+
*/
|
|
82
109
|
export const hasMatrixTransform = function (tlist) {
|
|
83
|
-
if (!tlist) {
|
|
110
|
+
if (!tlist) {
|
|
111
|
+
return false
|
|
112
|
+
}
|
|
84
113
|
let num = tlist.numberOfItems
|
|
85
114
|
while (num--) {
|
|
86
115
|
const xform = tlist.getItem(num)
|
|
87
|
-
if (xform.type === 1 && !isIdentity(xform.matrix)) {
|
|
116
|
+
if (xform.type === 1 && !isIdentity(xform.matrix)) {
|
|
117
|
+
return true
|
|
118
|
+
}
|
|
88
119
|
}
|
|
89
120
|
return false
|
|
90
121
|
}
|
|
91
122
|
|
|
92
123
|
/**
|
|
93
|
-
* @typedef {PlainObject} module:math.TransformedBox An object with the following values
|
|
94
|
-
* @property {module:math.XYObject} tl - The top left coordinate
|
|
95
|
-
* @property {module:math.XYObject} tr - The top right coordinate
|
|
96
|
-
* @property {module:math.XYObject} bl - The bottom left coordinate
|
|
97
|
-
* @property {module:math.XYObject} br - The bottom right coordinate
|
|
98
|
-
* @property {PlainObject} aabox - Object with the following values:
|
|
99
|
-
* @property {Float} aabox.x - Float with the axis-aligned x coordinate
|
|
100
|
-
* @property {Float} aabox.y - Float with the axis-aligned y coordinate
|
|
101
|
-
* @property {Float} aabox.width - Float with the axis-aligned width coordinate
|
|
102
|
-
* @property {Float} aabox.height - Float with the axis-aligned height coordinate
|
|
103
|
-
*/
|
|
124
|
+
* @typedef {PlainObject} module:math.TransformedBox An object with the following values
|
|
125
|
+
* @property {module:math.XYObject} tl - The top left coordinate
|
|
126
|
+
* @property {module:math.XYObject} tr - The top right coordinate
|
|
127
|
+
* @property {module:math.XYObject} bl - The bottom left coordinate
|
|
128
|
+
* @property {module:math.XYObject} br - The bottom right coordinate
|
|
129
|
+
* @property {PlainObject} aabox - Object with the following values:
|
|
130
|
+
* @property {Float} aabox.x - Float with the axis-aligned x coordinate
|
|
131
|
+
* @property {Float} aabox.y - Float with the axis-aligned y coordinate
|
|
132
|
+
* @property {Float} aabox.width - Float with the axis-aligned width coordinate
|
|
133
|
+
* @property {Float} aabox.height - Float with the axis-aligned height coordinate
|
|
134
|
+
*/
|
|
104
135
|
|
|
105
136
|
/**
|
|
106
137
|
* Transforms a rectangle based on the given matrix.
|
|
@@ -111,12 +142,12 @@ export const hasMatrixTransform = function (tlist) {
|
|
|
111
142
|
* @param {Float} h - Float with the box height
|
|
112
143
|
* @param {SVGMatrix} m - Matrix object to transform the box by
|
|
113
144
|
* @returns {module:math.TransformedBox}
|
|
114
|
-
*/
|
|
145
|
+
*/
|
|
115
146
|
export const transformBox = function (l, t, w, h, m) {
|
|
116
147
|
const tl = transformPoint(l, t, m)
|
|
117
|
-
const tr = transformPoint(
|
|
118
|
-
const bl = transformPoint(l,
|
|
119
|
-
const br = transformPoint(
|
|
148
|
+
const tr = transformPoint(l + w, t, m)
|
|
149
|
+
const bl = transformPoint(l, t + h, m)
|
|
150
|
+
const br = transformPoint(l + w, t + h, m)
|
|
120
151
|
|
|
121
152
|
const minx = Math.min(tl.x, tr.x, bl.x, br.x)
|
|
122
153
|
const maxx = Math.max(tl.x, tr.x, bl.x, br.x)
|
|
@@ -131,8 +162,8 @@ export const transformBox = function (l, t, w, h, m) {
|
|
|
131
162
|
aabox: {
|
|
132
163
|
x: minx,
|
|
133
164
|
y: miny,
|
|
134
|
-
width:
|
|
135
|
-
height:
|
|
165
|
+
width: maxx - minx,
|
|
166
|
+
height: maxy - miny
|
|
136
167
|
}
|
|
137
168
|
}
|
|
138
169
|
}
|
|
@@ -148,23 +179,28 @@ export const transformBox = function (l, t, w, h, m) {
|
|
|
148
179
|
* @param {Integer} [max] - Optional integer indicating end transform position;
|
|
149
180
|
* defaults to one less than the tlist's `numberOfItems`
|
|
150
181
|
* @returns {SVGTransform} A single matrix transform object
|
|
151
|
-
*/
|
|
182
|
+
*/
|
|
152
183
|
export const transformListToTransform = function (tlist, min, max) {
|
|
153
184
|
if (!tlist) {
|
|
154
185
|
// Or should tlist = null have been prevented before this?
|
|
155
186
|
return svg.createSVGTransformFromMatrix(svg.createSVGMatrix())
|
|
156
187
|
}
|
|
157
188
|
min = min || 0
|
|
158
|
-
max = max ||
|
|
189
|
+
max = max || tlist.numberOfItems - 1
|
|
159
190
|
min = Number.parseInt(min)
|
|
160
191
|
max = Number.parseInt(max)
|
|
161
|
-
if (min > max) {
|
|
192
|
+
if (min > max) {
|
|
193
|
+
const temp = max
|
|
194
|
+
max = min
|
|
195
|
+
min = temp
|
|
196
|
+
}
|
|
162
197
|
let m = svg.createSVGMatrix()
|
|
163
198
|
for (let i = min; i <= max; ++i) {
|
|
164
199
|
// if our indices are out of range, just use a harmless identity matrix
|
|
165
|
-
const mtom =
|
|
166
|
-
|
|
167
|
-
|
|
200
|
+
const mtom =
|
|
201
|
+
i >= 0 && i < tlist.numberOfItems
|
|
202
|
+
? tlist.getItem(i).matrix
|
|
203
|
+
: svg.createSVGMatrix()
|
|
168
204
|
m = matrixMultiply(m, mtom)
|
|
169
205
|
}
|
|
170
206
|
return svg.createSVGTransformFromMatrix(m)
|
|
@@ -175,9 +211,9 @@ export const transformListToTransform = function (tlist, min, max) {
|
|
|
175
211
|
* @function module:math.getMatrix
|
|
176
212
|
* @param {Element} elem - The DOM element to check
|
|
177
213
|
* @returns {SVGMatrix} The matrix object associated with the element's transformlist
|
|
178
|
-
*/
|
|
214
|
+
*/
|
|
179
215
|
export const getMatrix = (elem) => {
|
|
180
|
-
const tlist = elem
|
|
216
|
+
const tlist = getTransformList(elem)
|
|
181
217
|
return transformListToTransform(tlist).matrix
|
|
182
218
|
}
|
|
183
219
|
|
|
@@ -190,7 +226,7 @@ export const getMatrix = (elem) => {
|
|
|
190
226
|
* @param {Integer} x2 - Second coordinate's x value
|
|
191
227
|
* @param {Integer} y2 - Second coordinate's y value
|
|
192
228
|
* @returns {module:math.AngleCoord45}
|
|
193
|
-
*/
|
|
229
|
+
*/
|
|
194
230
|
export const snapToAngle = (x1, y1, x2, y2) => {
|
|
195
231
|
const snap = Math.PI / 4 // 45 degrees
|
|
196
232
|
const dx = x2 - x1
|
|
@@ -214,8 +250,10 @@ export const snapToAngle = (x1, y1, x2, y2) => {
|
|
|
214
250
|
* @returns {boolean} True if rectangles intersect
|
|
215
251
|
*/
|
|
216
252
|
export const rectsIntersect = (r1, r2) => {
|
|
217
|
-
return
|
|
218
|
-
|
|
219
|
-
r2.
|
|
220
|
-
|
|
253
|
+
return (
|
|
254
|
+
r2.x < r1.x + r1.width &&
|
|
255
|
+
r2.x + r2.width > r1.x &&
|
|
256
|
+
r2.y < r1.y + r1.height &&
|
|
257
|
+
r2.y + r2.height > r1.y
|
|
258
|
+
)
|
|
221
259
|
}
|
package/core/path-actions.js
CHANGED
|
@@ -11,7 +11,7 @@ import { shortFloat } from './units.js'
|
|
|
11
11
|
import { ChangeElementCommand, BatchCommand } from './history.js'
|
|
12
12
|
import {
|
|
13
13
|
transformPoint, snapToAngle, rectsIntersect,
|
|
14
|
-
transformListToTransform
|
|
14
|
+
transformListToTransform, getTransformList
|
|
15
15
|
} from './math.js'
|
|
16
16
|
import {
|
|
17
17
|
assignAttributes, getElement, getRotationAngle, snapToGrid,
|
|
@@ -876,7 +876,7 @@ export const pathActionsMethod = (function () {
|
|
|
876
876
|
*/
|
|
877
877
|
resetOrientation (pth) {
|
|
878
878
|
if (pth?.nodeName !== 'path') { return false }
|
|
879
|
-
const tlist = pth
|
|
879
|
+
const tlist = getTransformList(pth)
|
|
880
880
|
const m = transformListToTransform(tlist).matrix
|
|
881
881
|
tlist.clear()
|
|
882
882
|
pth.removeAttribute('transform')
|
package/core/path.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { shortFloat } from './units.js'
|
|
10
|
-
import { transformPoint } from './math.js'
|
|
10
|
+
import { transformPoint, getTransformList } from './math.js'
|
|
11
11
|
import {
|
|
12
12
|
getRotationAngle, getBBox,
|
|
13
13
|
getRefElem, findDefs,
|
|
@@ -511,7 +511,7 @@ export const recalcRotatedPath = () => {
|
|
|
511
511
|
|
|
512
512
|
// now we must set the new transform to be rotated around the new center
|
|
513
513
|
const Rnc = svgCanvas.getSvgRoot().createSVGTransform()
|
|
514
|
-
const tlist = currentPath
|
|
514
|
+
const tlist = getTransformList(currentPath)
|
|
515
515
|
Rnc.setRotate((angle * 180.0 / Math.PI), newcx, newcy)
|
|
516
516
|
tlist.replaceItem(Rnc, 0)
|
|
517
517
|
}
|