@svgedit/svgcanvas 7.1.4

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/math.js ADDED
@@ -0,0 +1,221 @@
1
+ /**
2
+ * Mathematical utilities.
3
+ * @module math
4
+ * @license MIT
5
+ *
6
+ * @copyright 2010 Alexis Deveria, 2010 Jeff Schiller
7
+ */
8
+
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
+ */
15
+
16
+ /**
17
+ * @typedef {PlainObject} module:math.XYObject
18
+ * @property {Float} x
19
+ * @property {Float} y
20
+ */
21
+
22
+ import { NS } from './namespaces.js'
23
+
24
+ // Constants
25
+ const NEAR_ZERO = 1e-14
26
+
27
+ // Throw away SVGSVGElement used for creating matrices/transforms.
28
+ const svg = document.createElementNS(NS.SVG, 'svg')
29
+
30
+ /**
31
+ * A (hopefully) quicker function to transform a point by a matrix
32
+ * (this function avoids any DOM calls and just does the math).
33
+ * @function module:math.transformPoint
34
+ * @param {Float} x - Float representing the x coordinate
35
+ * @param {Float} y - Float representing the y coordinate
36
+ * @param {SVGMatrix} m - Matrix object to transform the point with
37
+ * @returns {module:math.XYObject} An x, y object representing the transformed point
38
+ */
39
+ export const transformPoint = function (x, y, m) {
40
+ return { x: m.a * x + m.c * y + m.e, y: m.b * x + m.d * y + m.f }
41
+ }
42
+
43
+ /**
44
+ * Helper function to check if the matrix performs no actual transform
45
+ * (i.e. exists for identity purposes).
46
+ * @function module:math.isIdentity
47
+ * @param {SVGMatrix} m - The matrix object to check
48
+ * @returns {boolean} Indicates whether or not the matrix is 1,0,0,1,0,0
49
+ */
50
+ export const isIdentity = function (m) {
51
+ return (m.a === 1 && m.b === 0 && m.c === 0 && m.d === 1 && m.e === 0 && m.f === 0)
52
+ }
53
+
54
+ /**
55
+ * This function tries to return a `SVGMatrix` that is the multiplication `m1 * m2`.
56
+ * We also round to zero when it's near zero.
57
+ * @function module:math.matrixMultiply
58
+ * @param {...SVGMatrix} args - Matrix objects to multiply
59
+ * @returns {SVGMatrix} The matrix object resulting from the calculation
60
+ */
61
+ export const matrixMultiply = function (...args) {
62
+ const m = args.reduceRight((prev, m1) => {
63
+ return m1.multiply(prev)
64
+ })
65
+
66
+ if (Math.abs(m.a) < NEAR_ZERO) { m.a = 0 }
67
+ if (Math.abs(m.b) < NEAR_ZERO) { m.b = 0 }
68
+ if (Math.abs(m.c) < NEAR_ZERO) { m.c = 0 }
69
+ if (Math.abs(m.d) < NEAR_ZERO) { m.d = 0 }
70
+ if (Math.abs(m.e) < NEAR_ZERO) { m.e = 0 }
71
+ if (Math.abs(m.f) < NEAR_ZERO) { m.f = 0 }
72
+
73
+ return m
74
+ }
75
+
76
+ /**
77
+ * See if the given transformlist includes a non-indentity matrix transform.
78
+ * @function module:math.hasMatrixTransform
79
+ * @param {SVGTransformList} [tlist] - The transformlist to check
80
+ * @returns {boolean} Whether or not a matrix transform was found
81
+ */
82
+ export const hasMatrixTransform = function (tlist) {
83
+ if (!tlist) { return false }
84
+ let num = tlist.numberOfItems
85
+ while (num--) {
86
+ const xform = tlist.getItem(num)
87
+ if (xform.type === 1 && !isIdentity(xform.matrix)) { return true }
88
+ }
89
+ return false
90
+ }
91
+
92
+ /**
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
+ */
104
+
105
+ /**
106
+ * Transforms a rectangle based on the given matrix.
107
+ * @function module:math.transformBox
108
+ * @param {Float} l - Float with the box's left coordinate
109
+ * @param {Float} t - Float with the box's top coordinate
110
+ * @param {Float} w - Float with the box width
111
+ * @param {Float} h - Float with the box height
112
+ * @param {SVGMatrix} m - Matrix object to transform the box by
113
+ * @returns {module:math.TransformedBox}
114
+ */
115
+ export const transformBox = function (l, t, w, h, m) {
116
+ const tl = transformPoint(l, t, m)
117
+ const tr = transformPoint((l + w), t, m)
118
+ const bl = transformPoint(l, (t + h), m)
119
+ const br = transformPoint((l + w), (t + h), m)
120
+
121
+ const minx = Math.min(tl.x, tr.x, bl.x, br.x)
122
+ const maxx = Math.max(tl.x, tr.x, bl.x, br.x)
123
+ const miny = Math.min(tl.y, tr.y, bl.y, br.y)
124
+ const maxy = Math.max(tl.y, tr.y, bl.y, br.y)
125
+
126
+ return {
127
+ tl,
128
+ tr,
129
+ bl,
130
+ br,
131
+ aabox: {
132
+ x: minx,
133
+ y: miny,
134
+ width: (maxx - minx),
135
+ height: (maxy - miny)
136
+ }
137
+ }
138
+ }
139
+
140
+ /**
141
+ * This returns a single matrix Transform for a given Transform List
142
+ * (this is the equivalent of `SVGTransformList.consolidate()` but unlike
143
+ * that method, this one does not modify the actual `SVGTransformList`).
144
+ * This function is very liberal with its `min`, `max` arguments.
145
+ * @function module:math.transformListToTransform
146
+ * @param {SVGTransformList} tlist - The transformlist object
147
+ * @param {Integer} [min=0] - Optional integer indicating start transform position
148
+ * @param {Integer} [max] - Optional integer indicating end transform position;
149
+ * defaults to one less than the tlist's `numberOfItems`
150
+ * @returns {SVGTransform} A single matrix transform object
151
+ */
152
+ export const transformListToTransform = function (tlist, min, max) {
153
+ if (!tlist) {
154
+ // Or should tlist = null have been prevented before this?
155
+ return svg.createSVGTransformFromMatrix(svg.createSVGMatrix())
156
+ }
157
+ min = min || 0
158
+ max = max || (tlist.numberOfItems - 1)
159
+ min = Number.parseInt(min)
160
+ max = Number.parseInt(max)
161
+ if (min > max) { const temp = max; max = min; min = temp }
162
+ let m = svg.createSVGMatrix()
163
+ for (let i = min; i <= max; ++i) {
164
+ // if our indices are out of range, just use a harmless identity matrix
165
+ const mtom = (i >= 0 && i < tlist.numberOfItems
166
+ ? tlist.getItem(i).matrix
167
+ : svg.createSVGMatrix())
168
+ m = matrixMultiply(m, mtom)
169
+ }
170
+ return svg.createSVGTransformFromMatrix(m)
171
+ }
172
+
173
+ /**
174
+ * Get the matrix object for a given element.
175
+ * @function module:math.getMatrix
176
+ * @param {Element} elem - The DOM element to check
177
+ * @returns {SVGMatrix} The matrix object associated with the element's transformlist
178
+ */
179
+ export const getMatrix = (elem) => {
180
+ const tlist = elem.transform.baseVal
181
+ return transformListToTransform(tlist).matrix
182
+ }
183
+
184
+ /**
185
+ * Returns a 45 degree angle coordinate associated with the two given
186
+ * coordinates.
187
+ * @function module:math.snapToAngle
188
+ * @param {Integer} x1 - First coordinate's x value
189
+ * @param {Integer} y1 - First coordinate's y value
190
+ * @param {Integer} x2 - Second coordinate's x value
191
+ * @param {Integer} y2 - Second coordinate's y value
192
+ * @returns {module:math.AngleCoord45}
193
+ */
194
+ export const snapToAngle = (x1, y1, x2, y2) => {
195
+ const snap = Math.PI / 4 // 45 degrees
196
+ const dx = x2 - x1
197
+ const dy = y2 - y1
198
+ const angle = Math.atan2(dy, dx)
199
+ const dist = Math.sqrt(dx * dx + dy * dy)
200
+ const snapangle = Math.round(angle / snap) * snap
201
+
202
+ return {
203
+ x: x1 + dist * Math.cos(snapangle),
204
+ y: y1 + dist * Math.sin(snapangle),
205
+ a: snapangle
206
+ }
207
+ }
208
+
209
+ /**
210
+ * Check if two rectangles (BBoxes objects) intersect each other.
211
+ * @function module:math.rectsIntersect
212
+ * @param {SVGRect} r1 - The first BBox-like object
213
+ * @param {SVGRect} r2 - The second BBox-like object
214
+ * @returns {boolean} True if rectangles intersect
215
+ */
216
+ export const rectsIntersect = (r1, r2) => {
217
+ return r2.x < (r1.x + r1.width) &&
218
+ (r2.x + r2.width) > r1.x &&
219
+ r2.y < (r1.y + r1.height) &&
220
+ (r2.y + r2.height) > r1.y
221
+ }
package/namespaces.js ADDED
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Namespaces or tools therefor.
3
+ * @module namespaces
4
+ * @license MIT
5
+ */
6
+
7
+ /**
8
+ * Common namepaces constants in alpha order.
9
+ * @enum {string}
10
+ * @type {PlainObject}
11
+ * @memberof module:namespaces
12
+ */
13
+ export const NS = {
14
+ HTML: 'http://www.w3.org/1999/xhtml',
15
+ MATH: 'http://www.w3.org/1998/Math/MathML',
16
+ SE: 'http://svg-edit.googlecode.com',
17
+ SVG: 'http://www.w3.org/2000/svg',
18
+ XLINK: 'http://www.w3.org/1999/xlink',
19
+ OI: 'http://www.optimistik.fr/namespace/svg/OIdata',
20
+ XML: 'http://www.w3.org/XML/1998/namespace',
21
+ XMLNS: 'http://www.w3.org/2000/xmlns/' // see http://www.w3.org/TR/REC-xml-names/#xmlReserved
22
+ // SODIPODI: 'http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd',
23
+ // INKSCAPE: 'http://www.inkscape.org/namespaces/inkscape',
24
+ // RDF: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
25
+ // OSB: 'http://www.openswatchbook.org/uri/2009/osb',
26
+ // CC: 'http://creativecommons.org/ns#',
27
+ // DC: 'http://purl.org/dc/elements/1.1/'
28
+ }
29
+
30
+ /**
31
+ * @function module:namespaces.getReverseNS
32
+ * @returns {string} The NS with key values switched and lowercase
33
+ */
34
+ export const getReverseNS = function () {
35
+ const reverseNS = {}
36
+ Object.entries(NS).forEach(([name, URI]) => {
37
+ reverseNS[URI] = name.toLowerCase()
38
+ })
39
+ return reverseNS
40
+ }
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@svgedit/svgcanvas",
3
+ "version": "7.1.4",
4
+ "description": "SVG Canvas",
5
+ "main": "dist/svgcanvas.js",
6
+ "author": "Narendra Sisodiya",
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "bugs": {
11
+ "url": "https://github.com/SVG-Edit/svgedit/issues"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/SVG-Edit/svgedit.git"
16
+ },
17
+ "homepage": "https://github.com/SVG-Edit/svgedit#readme",
18
+ "contributors": [
19
+ "Pavol Rusnak",
20
+ "Jeff Schiller",
21
+ "Vidar Hokstad",
22
+ "Alexis Deveria",
23
+ "Brett Zamir",
24
+ "Fabien Jacq",
25
+ "OptimistikSAS"
26
+ ],
27
+ "keywords": [
28
+ "svg-editor",
29
+ "javascript",
30
+ "svg-edit",
31
+ "svg",
32
+ "svgcanvas"
33
+ ],
34
+ "license": "MIT",
35
+ "browserslist": [
36
+ "defaults",
37
+ "not IE 11",
38
+ "not OperaMini all"
39
+ ],
40
+ "standard": {
41
+ "ignore": [],
42
+ "globals": [
43
+ "cy",
44
+ "assert"
45
+ ],
46
+ "env": [
47
+ "mocha",
48
+ "browser"
49
+ ]
50
+ },
51
+ "scripts": {
52
+ "build": "rollup -c"
53
+ }
54
+ }
package/paint.js ADDED
@@ -0,0 +1,88 @@
1
+ /**
2
+ *
3
+ */
4
+ export default class Paint {
5
+ /**
6
+ * @param {module:jGraduate.jGraduatePaintOptions} [opt]
7
+ */
8
+ constructor (opt) {
9
+ const options = opt || {}
10
+ this.alpha = isNaN(options.alpha) ? 100 : options.alpha
11
+ // copy paint object
12
+ if (options.copy) {
13
+ /**
14
+ * @name module:jGraduate~Paint#type
15
+ * @type {"none"|"solidColor"|"linearGradient"|"radialGradient"}
16
+ */
17
+ this.type = options.copy.type
18
+ /**
19
+ * Represents opacity (0-100).
20
+ * @name module:jGraduate~Paint#alpha
21
+ * @type {Float}
22
+ */
23
+ this.alpha = options.copy.alpha
24
+ /**
25
+ * Represents #RRGGBB hex of color.
26
+ * @name module:jGraduate~Paint#solidColor
27
+ * @type {string}
28
+ */
29
+ this.solidColor = null
30
+ /**
31
+ * @name module:jGraduate~Paint#linearGradient
32
+ * @type {SVGLinearGradientElement}
33
+ */
34
+ this.linearGradient = null
35
+ /**
36
+ * @name module:jGraduate~Paint#radialGradient
37
+ * @type {SVGRadialGradientElement}
38
+ */
39
+ this.radialGradient = null
40
+
41
+ switch (this.type) {
42
+ case 'none':
43
+ break
44
+ case 'solidColor':
45
+ this.solidColor = options.copy.solidColor
46
+ break
47
+ case 'linearGradient':
48
+ this.linearGradient = options.copy.linearGradient.cloneNode(true)
49
+ break
50
+ case 'radialGradient':
51
+ this.radialGradient = options.copy.radialGradient.cloneNode(true)
52
+ break
53
+ }
54
+ // create linear gradient paint
55
+ } else if (options.linearGradient) {
56
+ this.type = 'linearGradient'
57
+ this.solidColor = null
58
+ this.radialGradient = null
59
+ if (options.linearGradient.hasAttribute('xlink:href')) {
60
+ const xhref = document.getElementById(options.linearGradient.getAttribute('xlink:href').substr(1))
61
+ this.linearGradient = xhref.cloneNode(true)
62
+ } else {
63
+ this.linearGradient = options.linearGradient.cloneNode(true)
64
+ }
65
+ // create linear gradient paint
66
+ } else if (options.radialGradient) {
67
+ this.type = 'radialGradient'
68
+ this.solidColor = null
69
+ this.linearGradient = null
70
+ if (options.radialGradient.hasAttribute('xlink:href')) {
71
+ const xhref = document.getElementById(options.radialGradient.getAttribute('xlink:href').substr(1))
72
+ this.radialGradient = xhref.cloneNode(true)
73
+ } else {
74
+ this.radialGradient = options.radialGradient.cloneNode(true)
75
+ }
76
+ // create solid color paint
77
+ } else if (options.solidColor) {
78
+ this.type = 'solidColor'
79
+ this.solidColor = options.solidColor
80
+ // create empty paint
81
+ } else {
82
+ this.type = 'none'
83
+ this.solidColor = null
84
+ this.linearGradient = null
85
+ this.radialGradient = null
86
+ }
87
+ }
88
+ }
package/paste-elem.js ADDED
@@ -0,0 +1,127 @@
1
+ import {
2
+ getStrokedBBoxDefaultVisible
3
+ } from './utilities.js'
4
+ import * as hstry from './history.js'
5
+
6
+ const {
7
+ InsertElementCommand, BatchCommand
8
+ } = hstry
9
+
10
+ let svgCanvas = null
11
+
12
+ /**
13
+ * @function module:paste-elem.init
14
+ * @param {module:paste-elem.pasteContext} pasteContext
15
+ * @returns {void}
16
+ */
17
+ export const init = (canvas) => {
18
+ svgCanvas = canvas
19
+ }
20
+
21
+ /**
22
+ * @function module:svgcanvas.SvgCanvas#pasteElements
23
+ * @param {"in_place"|"point"|void} type
24
+ * @param {Integer|void} x Expected if type is "point"
25
+ * @param {Integer|void} y Expected if type is "point"
26
+ * @fires module:svgcanvas.SvgCanvas#event:changed
27
+ * @fires module:svgcanvas.SvgCanvas#event:ext_IDsUpdated
28
+ * @returns {void}
29
+ */
30
+ export const pasteElementsMethod = function (type, x, y) {
31
+ let clipb = JSON.parse(sessionStorage.getItem(svgCanvas.getClipboardID()))
32
+ if (!clipb) return
33
+ let len = clipb.length
34
+ if (!len) return
35
+
36
+ const pasted = []
37
+ const batchCmd = new BatchCommand('Paste elements')
38
+ // const drawing = getCurrentDrawing();
39
+ /**
40
+ * @typedef {PlainObject<string, string>} module:svgcanvas.ChangedIDs
41
+ */
42
+ /**
43
+ * @type {module:svgcanvas.ChangedIDs}
44
+ */
45
+ const changedIDs = {}
46
+
47
+ // Recursively replace IDs and record the changes
48
+ /**
49
+ *
50
+ * @param {module:svgcanvas.SVGAsJSON} elem
51
+ * @returns {void}
52
+ */
53
+ function checkIDs (elem) {
54
+ if (elem.attr?.id) {
55
+ changedIDs[elem.attr.id] = svgCanvas.getNextId()
56
+ elem.attr.id = changedIDs[elem.attr.id]
57
+ }
58
+ if (elem.children) elem.children.forEach((child) => checkIDs(child))
59
+ }
60
+ clipb.forEach((elem) => checkIDs(elem))
61
+
62
+ // Give extensions like the connector extension a chance to reflect new IDs and remove invalid elements
63
+ /**
64
+ * Triggered when `pasteElements` is called from a paste action (context menu or key).
65
+ * @event module:svgcanvas.SvgCanvas#event:ext_IDsUpdated
66
+ * @type {PlainObject}
67
+ * @property {module:svgcanvas.SVGAsJSON[]} elems
68
+ * @property {module:svgcanvas.ChangedIDs} changes Maps past ID (on attribute) to current ID
69
+ */
70
+ svgCanvas.runExtensions(
71
+ 'IDsUpdated',
72
+ /** @type {module:svgcanvas.SvgCanvas#event:ext_IDsUpdated} */
73
+ { elems: clipb, changes: changedIDs },
74
+ true
75
+ ).forEach(function (extChanges) {
76
+ if (!extChanges || !('remove' in extChanges)) return
77
+
78
+ extChanges.remove.forEach(function (removeID) {
79
+ clipb = clipb.filter(function (clipBoardItem) {
80
+ return clipBoardItem.attr.id !== removeID
81
+ })
82
+ })
83
+ })
84
+
85
+ // Move elements to lastClickPoint
86
+ while (len--) {
87
+ const elem = clipb[len]
88
+ if (!elem) { continue }
89
+
90
+ const copy = svgCanvas.addSVGElementsFromJson(elem)
91
+ pasted.push(copy)
92
+ batchCmd.addSubCommand(new InsertElementCommand(copy))
93
+
94
+ svgCanvas.restoreRefElements(copy)
95
+ }
96
+
97
+ svgCanvas.selectOnly(pasted)
98
+
99
+ if (type !== 'in_place') {
100
+ let ctrX; let ctrY
101
+
102
+ if (!type) {
103
+ ctrX = svgCanvas.getLastClickPoint('x')
104
+ ctrY = svgCanvas.getLastClickPoint('y')
105
+ } else if (type === 'point') {
106
+ ctrX = x
107
+ ctrY = y
108
+ }
109
+
110
+ const bbox = getStrokedBBoxDefaultVisible(pasted)
111
+ const cx = ctrX - (bbox.x + bbox.width / 2)
112
+ const cy = ctrY - (bbox.y + bbox.height / 2)
113
+ const dx = []
114
+ const dy = []
115
+
116
+ pasted.forEach(function (_item) {
117
+ dx.push(cx)
118
+ dy.push(cy)
119
+ })
120
+
121
+ const cmd = svgCanvas.moveSelectedElements(dx, dy, false)
122
+ if (cmd) batchCmd.addSubCommand(cmd)
123
+ }
124
+
125
+ svgCanvas.addCommandToHistory(batchCmd)
126
+ svgCanvas.call('changed', pasted)
127
+ }