@svgedit/svgcanvas 7.2.6 → 7.4.1
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 +6 -0
- package/common/browser.js +104 -37
- package/common/logger.js +151 -0
- package/common/util.js +96 -155
- package/core/blur-event.js +106 -42
- package/core/clear.js +13 -3
- package/core/coords.js +214 -90
- package/core/copy-elem.js +27 -13
- package/core/dataStorage.js +84 -21
- package/core/draw.js +80 -40
- package/core/elem-get-set.js +161 -77
- package/core/event.js +143 -28
- package/core/history.js +51 -31
- package/core/historyrecording.js +4 -2
- package/core/json.js +54 -12
- package/core/layer.js +11 -17
- package/core/math.js +102 -23
- package/core/namespaces.js +5 -5
- package/core/paint.js +100 -23
- package/core/paste-elem.js +58 -19
- package/core/path-actions.js +812 -791
- package/core/path-method.js +236 -37
- package/core/path.js +45 -10
- package/core/recalculate.js +438 -24
- package/core/sanitize.js +71 -34
- package/core/select.js +44 -20
- package/core/selected-elem.js +146 -31
- package/core/selection.js +16 -6
- package/core/svg-exec.js +103 -29
- package/core/svgroot.js +1 -1
- package/core/text-actions.js +327 -306
- package/core/undo.js +20 -5
- package/core/units.js +8 -6
- package/core/utilities.js +316 -203
- package/dist/svgcanvas.js +31616 -53281
- package/dist/svgcanvas.js.map +1 -1
- package/package.json +55 -54
- package/publish.md +1 -6
- package/svgcanvas.d.ts +225 -0
- package/svgcanvas.js +9 -9
- package/vite.config.mjs +20 -0
- package/rollup.config.mjs +0 -38
package/core/path-actions.js
CHANGED
|
@@ -38,7 +38,7 @@ export const init = (canvas) => {
|
|
|
38
38
|
* @param {boolean} toRel - true of convert to relative
|
|
39
39
|
* @returns {string}
|
|
40
40
|
*/
|
|
41
|
-
export const convertPath =
|
|
41
|
+
export const convertPath = (pth, toRel) => {
|
|
42
42
|
const { pathSegList } = pth
|
|
43
43
|
const len = pathSegList.numberOfItems
|
|
44
44
|
let curx = 0; let cury = 0
|
|
@@ -64,7 +64,7 @@ export const convertPath = function (pth, toRel) {
|
|
|
64
64
|
case 'z': // z,Z closepath (Z/z)
|
|
65
65
|
case 'Z':
|
|
66
66
|
d += 'z'
|
|
67
|
-
if (lastM
|
|
67
|
+
if (lastM) {
|
|
68
68
|
curx = lastM[0]
|
|
69
69
|
cury = lastM[1]
|
|
70
70
|
}
|
|
@@ -217,34 +217,27 @@ export const convertPath = function (pth, toRel) {
|
|
|
217
217
|
* @param {Integer[]} [lastPoint] - x,y point
|
|
218
218
|
* @returns {string}
|
|
219
219
|
*/
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
points
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}
|
|
228
|
-
if (lastPoint) {
|
|
229
|
-
segment += ' ' + shortFloat(lastPoint)
|
|
230
|
-
}
|
|
231
|
-
return segment
|
|
220
|
+
const pathDSegment = (letter, points, morePoints, lastPoint) => {
|
|
221
|
+
const parts = [
|
|
222
|
+
letter + points.map(pnt => shortFloat(pnt)).join(' '),
|
|
223
|
+
morePoints ? morePoints.join(' ') : null,
|
|
224
|
+
lastPoint ? shortFloat(lastPoint) : null
|
|
225
|
+
].filter(Boolean)
|
|
226
|
+
return parts.join(' ')
|
|
232
227
|
}
|
|
233
228
|
|
|
234
229
|
/**
|
|
235
230
|
* Group: Path edit functions.
|
|
236
231
|
* Functions relating to editing path elements.
|
|
237
|
-
* @
|
|
232
|
+
* @class PathActions
|
|
238
233
|
* @memberof module:path
|
|
239
234
|
*/
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
// No `svgCanvas` yet but should be ok as is `null` by default
|
|
247
|
-
// svgCanvas.setDrawnPath(null);
|
|
235
|
+
class PathActions {
|
|
236
|
+
#subpath = false
|
|
237
|
+
#newPoint = null
|
|
238
|
+
#firstCtrl = null
|
|
239
|
+
#currentPath = null
|
|
240
|
+
#hasMoved = false
|
|
248
241
|
|
|
249
242
|
/**
|
|
250
243
|
* This function converts a polyline (created by the fh_path tool) into
|
|
@@ -253,9 +246,9 @@ export const pathActionsMethod = (function () {
|
|
|
253
246
|
* @function smoothPolylineIntoPath
|
|
254
247
|
* @param {Element} element
|
|
255
248
|
* @returns {Element}
|
|
249
|
+
* @private
|
|
256
250
|
*/
|
|
257
|
-
|
|
258
|
-
let i
|
|
251
|
+
#smoothPolylineIntoPath = (element) => {
|
|
259
252
|
const { points } = element
|
|
260
253
|
const N = points.numberOfItems
|
|
261
254
|
if (N >= 4) {
|
|
@@ -272,9 +265,11 @@ export const pathActionsMethod = (function () {
|
|
|
272
265
|
// - https://www.codeproject.com/KB/graphics/BezierSpline.aspx?msg=2956963
|
|
273
266
|
// - https://www.ian-ko.com/ET_GeoWizards/UserGuide/smooth.htm
|
|
274
267
|
// - https://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/Bezier/bezier-der.html
|
|
275
|
-
let curpos = points.getItem(0)
|
|
268
|
+
let curpos = points.getItem(0)
|
|
269
|
+
let prevCtlPt = null
|
|
276
270
|
let d = []
|
|
277
|
-
d.push(
|
|
271
|
+
d.push(`M${curpos.x},${curpos.y} C`)
|
|
272
|
+
let i
|
|
278
273
|
for (i = 1; i <= (N - 4); i += 3) {
|
|
279
274
|
let ct1 = points.getItem(i)
|
|
280
275
|
const ct2 = points.getItem(i + 1)
|
|
@@ -321,917 +316,943 @@ export const pathActionsMethod = (function () {
|
|
|
321
316
|
return element
|
|
322
317
|
}
|
|
323
318
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
let mouseY = startY // Was this meant to work with the other `mouseY`? (was defined globally so adding `let` to at least avoid a global)
|
|
337
|
-
|
|
338
|
-
const zoom = svgCanvas.getZoom()
|
|
339
|
-
let x = mouseX / zoom
|
|
340
|
-
let y = mouseY / zoom
|
|
341
|
-
let stretchy = getElement('path_stretch_line')
|
|
342
|
-
newPoint = [x, y]
|
|
343
|
-
|
|
344
|
-
if (svgCanvas.getGridSnapping()) {
|
|
345
|
-
x = snapToGrid(x)
|
|
346
|
-
y = snapToGrid(y)
|
|
347
|
-
mouseX = snapToGrid(mouseX)
|
|
348
|
-
mouseY = snapToGrid(mouseY)
|
|
349
|
-
}
|
|
319
|
+
/**
|
|
320
|
+
* @param {MouseEvent} evt
|
|
321
|
+
* @param {Element} mouseTarget
|
|
322
|
+
* @param {Float} startX
|
|
323
|
+
* @param {Float} startY
|
|
324
|
+
* @returns {boolean|void}
|
|
325
|
+
*/
|
|
326
|
+
mouseDown (evt, mouseTarget, startX, startY) {
|
|
327
|
+
let id
|
|
328
|
+
if (svgCanvas.getCurrentMode() === 'path') {
|
|
329
|
+
let mouseX = startX // Was this meant to work with the other `mouseX`? (was defined globally so adding `let` to at least avoid a global)
|
|
330
|
+
let mouseY = startY // Was this meant to work with the other `mouseY`? (was defined globally so adding `let` to at least avoid a global)
|
|
350
331
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
332
|
+
const zoom = svgCanvas.getZoom()
|
|
333
|
+
let x = mouseX / zoom
|
|
334
|
+
let y = mouseY / zoom
|
|
335
|
+
let stretchy = getElement('path_stretch_line')
|
|
336
|
+
this.#newPoint = [x, y]
|
|
337
|
+
|
|
338
|
+
if (svgCanvas.getGridSnapping()) {
|
|
339
|
+
x = snapToGrid(x)
|
|
340
|
+
y = snapToGrid(y)
|
|
341
|
+
mouseX = snapToGrid(mouseX)
|
|
342
|
+
mouseY = snapToGrid(mouseY)
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (!stretchy) {
|
|
346
|
+
stretchy = document.createElementNS(NS.SVG, 'path')
|
|
347
|
+
assignAttributes(stretchy, {
|
|
348
|
+
id: 'path_stretch_line',
|
|
349
|
+
stroke: '#22C',
|
|
350
|
+
'stroke-width': '0.5',
|
|
351
|
+
fill: 'none'
|
|
352
|
+
})
|
|
353
|
+
getElement('selectorParentGroup').append(stretchy)
|
|
354
|
+
}
|
|
355
|
+
stretchy.setAttribute('display', 'inline')
|
|
356
|
+
|
|
357
|
+
let keep = null
|
|
358
|
+
let index
|
|
359
|
+
// if pts array is empty, create path element with M at current point
|
|
360
|
+
const drawnPath = svgCanvas.getDrawnPath()
|
|
361
|
+
if (!drawnPath) {
|
|
362
|
+
const dAttr = `M${x},${y} `
|
|
363
|
+
/* drawnPath = */ svgCanvas.setDrawnPath(svgCanvas.addSVGElementsFromJson({
|
|
364
|
+
element: 'path',
|
|
365
|
+
curStyles: true,
|
|
366
|
+
attr: {
|
|
367
|
+
d: dAttr,
|
|
368
|
+
id: svgCanvas.getNextId(),
|
|
369
|
+
opacity: svgCanvas.getOpacity() / 2
|
|
370
|
+
}
|
|
371
|
+
}))
|
|
372
|
+
// set stretchy line to first point
|
|
373
|
+
stretchy.setAttribute('d', `M${mouseX} ${mouseY} ${mouseX} ${mouseY}`)
|
|
374
|
+
index = this.#subpath ? path.segs.length : 0
|
|
375
|
+
svgCanvas.addPointGrip(index, mouseX, mouseY)
|
|
376
|
+
} else {
|
|
377
|
+
// determine if we clicked on an existing point
|
|
378
|
+
const seglist = drawnPath.pathSegList
|
|
379
|
+
let i = seglist.numberOfItems
|
|
380
|
+
const FUZZ = 6 / zoom
|
|
381
|
+
let clickOnPoint = false
|
|
382
|
+
while (i) {
|
|
383
|
+
i--
|
|
384
|
+
const item = seglist.getItem(i)
|
|
385
|
+
const px = item.x; const py = item.y
|
|
386
|
+
// found a matching point
|
|
387
|
+
if (x >= (px - FUZZ) && x <= (px + FUZZ) &&
|
|
394
388
|
y >= (py - FUZZ) && y <= (py + FUZZ)
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
389
|
+
) {
|
|
390
|
+
clickOnPoint = true
|
|
391
|
+
break
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// get path element that we are in the process of creating
|
|
396
|
+
id = svgCanvas.getId()
|
|
397
|
+
|
|
398
|
+
// Remove previous path object if previously created
|
|
399
|
+
svgCanvas.removePath_(id)
|
|
400
|
+
|
|
401
|
+
const newpath = getElement(id)
|
|
402
|
+
let newseg
|
|
403
|
+
let sSeg
|
|
404
|
+
const len = seglist.numberOfItems
|
|
405
|
+
// if we clicked on an existing point, then we are done this path, commit it
|
|
406
|
+
// (i, i+1) are the x,y that were clicked on
|
|
407
|
+
if (clickOnPoint) {
|
|
408
|
+
// if clicked on any other point but the first OR
|
|
409
|
+
// the first point was clicked on and there are less than 3 points
|
|
410
|
+
// then leave the path open
|
|
411
|
+
// otherwise, close the path
|
|
412
|
+
if (i <= 1 && len >= 2) {
|
|
413
|
+
// Create end segment
|
|
414
|
+
const absX = seglist.getItem(0).x
|
|
415
|
+
const absY = seglist.getItem(0).y
|
|
416
|
+
|
|
417
|
+
sSeg = stretchy.pathSegList.getItem(1)
|
|
418
|
+
newseg = sSeg.pathSegType === 4
|
|
419
|
+
? drawnPath.createSVGPathSegLinetoAbs(absX, absY)
|
|
420
|
+
: drawnPath.createSVGPathSegCurvetoCubicAbs(absX, absY, sSeg.x1 / zoom, sSeg.y1 / zoom, absX, absY)
|
|
421
|
+
|
|
422
|
+
const endseg = drawnPath.createSVGPathSegClosePath()
|
|
423
|
+
seglist.appendItem(newseg)
|
|
424
|
+
seglist.appendItem(endseg)
|
|
425
|
+
} else if (len < 3) {
|
|
426
|
+
keep = false
|
|
427
|
+
return keep
|
|
399
428
|
}
|
|
429
|
+
stretchy.remove()
|
|
400
430
|
|
|
401
|
-
//
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
let sSeg
|
|
410
|
-
const len = seglist.numberOfItems
|
|
411
|
-
// if we clicked on an existing point, then we are done this path, commit it
|
|
412
|
-
// (i, i+1) are the x,y that were clicked on
|
|
413
|
-
if (clickOnPoint) {
|
|
414
|
-
// if clicked on any other point but the first OR
|
|
415
|
-
// the first point was clicked on and there are less than 3 points
|
|
416
|
-
// then leave the path open
|
|
417
|
-
// otherwise, close the path
|
|
418
|
-
if (i <= 1 && len >= 2) {
|
|
419
|
-
// Create end segment
|
|
420
|
-
const absX = seglist.getItem(0).x
|
|
421
|
-
const absY = seglist.getItem(0).y
|
|
422
|
-
|
|
423
|
-
sSeg = stretchy.pathSegList.getItem(1)
|
|
424
|
-
newseg = sSeg.pathSegType === 4
|
|
425
|
-
? drawnPath.createSVGPathSegLinetoAbs(absX, absY)
|
|
426
|
-
: drawnPath.createSVGPathSegCurvetoCubicAbs(absX, absY, sSeg.x1 / zoom, sSeg.y1 / zoom, absX, absY)
|
|
427
|
-
|
|
428
|
-
const endseg = drawnPath.createSVGPathSegClosePath()
|
|
429
|
-
seglist.appendItem(newseg)
|
|
430
|
-
seglist.appendItem(endseg)
|
|
431
|
-
} else if (len < 3) {
|
|
432
|
-
keep = false
|
|
433
|
-
return keep
|
|
431
|
+
// This will signal to commit the path
|
|
432
|
+
// const element = newpath; // Other event handlers define own `element`, so this was probably not meant to interact with them or one which shares state (as there were none); I therefore adding a missing `var` to avoid a global
|
|
433
|
+
/* drawnPath = */ svgCanvas.setDrawnPath(null)
|
|
434
|
+
svgCanvas.setStarted(false)
|
|
435
|
+
|
|
436
|
+
if (this.#subpath) {
|
|
437
|
+
if (path.matrix) {
|
|
438
|
+
svgCanvas.remapElement(newpath, {}, path.matrix.inverse())
|
|
434
439
|
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
if (subpath) {
|
|
443
|
-
if (path.matrix) {
|
|
444
|
-
svgCanvas.remapElement(newpath, {}, path.matrix.inverse())
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
const newD = newpath.getAttribute('d')
|
|
448
|
-
const origD = path.elem.getAttribute('d')
|
|
449
|
-
path.elem.setAttribute('d', origD + newD)
|
|
450
|
-
newpath.parentNode.removeChild(newpath)
|
|
451
|
-
if (path.matrix) {
|
|
452
|
-
svgCanvas.recalcRotatedPath()
|
|
453
|
-
}
|
|
454
|
-
pathActionsMethod.toEditMode(path.elem)
|
|
455
|
-
path.selectPt()
|
|
456
|
-
return false
|
|
440
|
+
|
|
441
|
+
const newD = newpath.getAttribute('d')
|
|
442
|
+
const origD = path.elem.getAttribute('d')
|
|
443
|
+
path.elem.setAttribute('d', origD + newD)
|
|
444
|
+
newpath.parentNode.removeChild(newpath)
|
|
445
|
+
if (path.matrix) {
|
|
446
|
+
svgCanvas.recalcRotatedPath()
|
|
457
447
|
}
|
|
448
|
+
pathActionsMethod.toEditMode(path.elem)
|
|
449
|
+
path.selectPt()
|
|
450
|
+
return false
|
|
451
|
+
}
|
|
458
452
|
// else, create a new point, update path element
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
const num = drawnPath.pathSegList.numberOfItems
|
|
469
|
-
const last = drawnPath.pathSegList.getItem(num - 1)
|
|
470
|
-
const lastx = last.x; const lasty = last.y
|
|
453
|
+
} else {
|
|
454
|
+
// Checks if current target or parents are #svgcontent
|
|
455
|
+
if (!(svgCanvas.getContainer() !== svgCanvas.getMouseTarget(evt) && svgCanvas.getContainer().contains(
|
|
456
|
+
svgCanvas.getMouseTarget(evt)
|
|
457
|
+
))) {
|
|
458
|
+
// Clicked outside canvas, so don't make point
|
|
459
|
+
return false
|
|
460
|
+
}
|
|
471
461
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
}
|
|
462
|
+
const num = drawnPath.pathSegList.numberOfItems
|
|
463
|
+
const last = drawnPath.pathSegList.getItem(num - 1)
|
|
464
|
+
const lastx = last.x; const lasty = last.y
|
|
476
465
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
? drawnPath.createSVGPathSegLinetoAbs(svgCanvas.round(x), svgCanvas.round(y))
|
|
481
|
-
: drawnPath.createSVGPathSegCurvetoCubicAbs(
|
|
482
|
-
svgCanvas.round(x),
|
|
483
|
-
svgCanvas.round(y),
|
|
484
|
-
sSeg.x1 / zoom,
|
|
485
|
-
sSeg.y1 / zoom,
|
|
486
|
-
sSeg.x2 / zoom,
|
|
487
|
-
sSeg.y2 / zoom
|
|
488
|
-
)
|
|
489
|
-
|
|
490
|
-
drawnPath.pathSegList.appendItem(newseg)
|
|
491
|
-
|
|
492
|
-
x *= zoom
|
|
493
|
-
y *= zoom
|
|
494
|
-
|
|
495
|
-
// set stretchy line to latest point
|
|
496
|
-
stretchy.setAttribute('d', ['M', x, y, x, y].join(' '))
|
|
497
|
-
index = num
|
|
498
|
-
if (subpath) { index += path.segs.length }
|
|
499
|
-
svgCanvas.addPointGrip(index, x, y)
|
|
466
|
+
if (evt.shiftKey) {
|
|
467
|
+
const xya = snapToAngle(lastx, lasty, x, y);
|
|
468
|
+
({ x, y } = xya)
|
|
500
469
|
}
|
|
501
|
-
// keep = true;
|
|
502
|
-
}
|
|
503
470
|
|
|
504
|
-
|
|
471
|
+
// Use the segment defined by stretchy
|
|
472
|
+
sSeg = stretchy.pathSegList.getItem(1)
|
|
473
|
+
newseg = sSeg.pathSegType === 4
|
|
474
|
+
? drawnPath.createSVGPathSegLinetoAbs(svgCanvas.round(x), svgCanvas.round(y))
|
|
475
|
+
: drawnPath.createSVGPathSegCurvetoCubicAbs(
|
|
476
|
+
svgCanvas.round(x),
|
|
477
|
+
svgCanvas.round(y),
|
|
478
|
+
sSeg.x1 / zoom,
|
|
479
|
+
sSeg.y1 / zoom,
|
|
480
|
+
sSeg.x2 / zoom,
|
|
481
|
+
sSeg.y2 / zoom
|
|
482
|
+
)
|
|
483
|
+
|
|
484
|
+
drawnPath.pathSegList.appendItem(newseg)
|
|
485
|
+
|
|
486
|
+
x *= zoom
|
|
487
|
+
y *= zoom
|
|
488
|
+
|
|
489
|
+
// set stretchy line to latest point
|
|
490
|
+
stretchy.setAttribute('d', ['M', x, y, x, y].join(' '))
|
|
491
|
+
index = num
|
|
492
|
+
if (this.#subpath) { index += path.segs.length }
|
|
493
|
+
svgCanvas.addPointGrip(index, x, y)
|
|
494
|
+
}
|
|
495
|
+
// keep = true;
|
|
505
496
|
}
|
|
506
497
|
|
|
507
|
-
|
|
508
|
-
|
|
498
|
+
return undefined
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// TODO: Make sure currentPath isn't null at this point
|
|
502
|
+
if (!path) { return undefined }
|
|
509
503
|
|
|
510
|
-
|
|
504
|
+
path.storeD();
|
|
511
505
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
506
|
+
({ id } = evt.target)
|
|
507
|
+
let curPt
|
|
508
|
+
if (id.startsWith('pathpointgrip_')) {
|
|
509
|
+
// Select this point
|
|
510
|
+
curPt = path.cur_pt = Number.parseInt(id.slice(14))
|
|
511
|
+
path.dragging = [startX, startY]
|
|
512
|
+
const seg = path.segs[curPt]
|
|
519
513
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
}
|
|
526
|
-
path.addPtsToSelection(curPt)
|
|
527
|
-
} else if (seg.selected) {
|
|
528
|
-
path.removePtFromSelection(curPt)
|
|
529
|
-
} else {
|
|
530
|
-
path.addPtsToSelection(curPt)
|
|
514
|
+
// only clear selection if shift is not pressed (otherwise, add
|
|
515
|
+
// node to selection)
|
|
516
|
+
if (!evt.shiftKey) {
|
|
517
|
+
if (path.selected_pts.length <= 1 || !seg.selected) {
|
|
518
|
+
path.clearSelection()
|
|
531
519
|
}
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
curPt
|
|
537
|
-
const ctrlNum = Number(parts[1])
|
|
538
|
-
path.selectPt(curPt, ctrlNum)
|
|
520
|
+
path.addPtsToSelection(curPt)
|
|
521
|
+
} else if (seg.selected) {
|
|
522
|
+
path.removePtFromSelection(curPt)
|
|
523
|
+
} else {
|
|
524
|
+
path.addPtsToSelection(curPt)
|
|
539
525
|
}
|
|
526
|
+
} else if (id.startsWith('ctrlpointgrip_')) {
|
|
527
|
+
path.dragging = [startX, startY]
|
|
540
528
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
height: 0,
|
|
555
|
-
display: 'inline'
|
|
556
|
-
}, 100)
|
|
529
|
+
const parts = id.split('_')[1].split('c')
|
|
530
|
+
curPt = Number(parts[0])
|
|
531
|
+
const ctrlNum = Number(parts[1])
|
|
532
|
+
path.selectPt(curPt, ctrlNum)
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// Start selection box
|
|
536
|
+
if (!path.dragging) {
|
|
537
|
+
let rubberBox = svgCanvas.getRubberBox()
|
|
538
|
+
if (!rubberBox) {
|
|
539
|
+
rubberBox = svgCanvas.setRubberBox(
|
|
540
|
+
svgCanvas.selectorManager.getRubberBandBox()
|
|
541
|
+
)
|
|
557
542
|
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
543
|
+
const zoom = svgCanvas.getZoom()
|
|
544
|
+
assignAttributes(rubberBox, {
|
|
545
|
+
x: startX * zoom,
|
|
546
|
+
y: startY * zoom,
|
|
547
|
+
width: 0,
|
|
548
|
+
height: 0,
|
|
549
|
+
display: 'inline'
|
|
550
|
+
}, 100)
|
|
551
|
+
}
|
|
552
|
+
return undefined
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
561
556
|
* @param {Float} mouseX
|
|
562
557
|
* @param {Float} mouseY
|
|
563
558
|
* @returns {void}
|
|
564
559
|
*/
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
} else {
|
|
613
|
-
const last = seglist.getItem(index - 1)
|
|
614
|
-
let lastX = last.x
|
|
615
|
-
let lastY = last.y
|
|
616
|
-
|
|
617
|
-
if (last.pathSegType === 6) {
|
|
618
|
-
lastX += (lastX - last.x2)
|
|
619
|
-
lastY += (lastY - last.y2)
|
|
620
|
-
} else if (firstCtrl) {
|
|
621
|
-
lastX = firstCtrl[0] / zoom
|
|
622
|
-
lastY = firstCtrl[1] / zoom
|
|
623
|
-
}
|
|
624
|
-
svgCanvas.replacePathSeg(6, index, [ptX, ptY, lastX, lastY, altX, altY], drawnPath)
|
|
625
|
-
}
|
|
560
|
+
mouseMove (mouseX, mouseY) {
|
|
561
|
+
const zoom = svgCanvas.getZoom()
|
|
562
|
+
this.#hasMoved = true
|
|
563
|
+
const drawnPath = svgCanvas.getDrawnPath()
|
|
564
|
+
if (svgCanvas.getCurrentMode() === 'path') {
|
|
565
|
+
if (!drawnPath) { return }
|
|
566
|
+
const seglist = drawnPath.pathSegList
|
|
567
|
+
const index = seglist.numberOfItems - 1
|
|
568
|
+
|
|
569
|
+
if (this.#newPoint) {
|
|
570
|
+
// First point
|
|
571
|
+
// if (!index) { return; }
|
|
572
|
+
|
|
573
|
+
// Set control points
|
|
574
|
+
const pointGrip1 = svgCanvas.addCtrlGrip('1c1')
|
|
575
|
+
const pointGrip2 = svgCanvas.addCtrlGrip('0c2')
|
|
576
|
+
|
|
577
|
+
// dragging pointGrip1
|
|
578
|
+
pointGrip1.setAttribute('cx', mouseX)
|
|
579
|
+
pointGrip1.setAttribute('cy', mouseY)
|
|
580
|
+
pointGrip1.setAttribute('display', 'inline')
|
|
581
|
+
|
|
582
|
+
const ptX = this.#newPoint[0]
|
|
583
|
+
const ptY = this.#newPoint[1]
|
|
584
|
+
|
|
585
|
+
// set curve
|
|
586
|
+
// const seg = seglist.getItem(index);
|
|
587
|
+
const curX = mouseX / zoom
|
|
588
|
+
const curY = mouseY / zoom
|
|
589
|
+
const altX = (ptX + (ptX - curX))
|
|
590
|
+
const altY = (ptY + (ptY - curY))
|
|
591
|
+
|
|
592
|
+
pointGrip2.setAttribute('cx', altX * zoom)
|
|
593
|
+
pointGrip2.setAttribute('cy', altY * zoom)
|
|
594
|
+
pointGrip2.setAttribute('display', 'inline')
|
|
595
|
+
|
|
596
|
+
const ctrlLine = svgCanvas.getCtrlLine(1)
|
|
597
|
+
assignAttributes(ctrlLine, {
|
|
598
|
+
x1: mouseX,
|
|
599
|
+
y1: mouseY,
|
|
600
|
+
x2: altX * zoom,
|
|
601
|
+
y2: altY * zoom,
|
|
602
|
+
display: 'inline'
|
|
603
|
+
})
|
|
604
|
+
|
|
605
|
+
if (index === 0) {
|
|
606
|
+
this.#firstCtrl = [mouseX, mouseY]
|
|
626
607
|
} else {
|
|
627
|
-
const
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
stretchy
|
|
638
|
-
)
|
|
639
|
-
} else if (firstCtrl) {
|
|
640
|
-
svgCanvas.replacePathSeg(6, 1, [mouseX, mouseY, firstCtrl[0], firstCtrl[1], mouseX, mouseY], stretchy)
|
|
641
|
-
} else {
|
|
642
|
-
svgCanvas.replacePathSeg(4, 1, [mouseX, mouseY], stretchy)
|
|
643
|
-
}
|
|
608
|
+
const last = seglist.getItem(index - 1)
|
|
609
|
+
let lastX = last.x
|
|
610
|
+
let lastY = last.y
|
|
611
|
+
|
|
612
|
+
if (last.pathSegType === 6) {
|
|
613
|
+
lastX += (lastX - last.x2)
|
|
614
|
+
lastY += (lastY - last.y2)
|
|
615
|
+
} else if (this.#firstCtrl) {
|
|
616
|
+
lastX = this.#firstCtrl[0] / zoom
|
|
617
|
+
lastY = this.#firstCtrl[1] / zoom
|
|
644
618
|
}
|
|
645
|
-
|
|
646
|
-
return
|
|
647
|
-
}
|
|
648
|
-
// if we are dragging a point, let's move it
|
|
649
|
-
if (path.dragging) {
|
|
650
|
-
const pt = svgCanvas.getPointFromGrip({
|
|
651
|
-
x: path.dragging[0],
|
|
652
|
-
y: path.dragging[1]
|
|
653
|
-
}, path)
|
|
654
|
-
const mpt = svgCanvas.getPointFromGrip({
|
|
655
|
-
x: mouseX,
|
|
656
|
-
y: mouseY
|
|
657
|
-
}, path)
|
|
658
|
-
const diffX = mpt.x - pt.x
|
|
659
|
-
const diffY = mpt.y - pt.y
|
|
660
|
-
path.dragging = [mouseX, mouseY]
|
|
661
|
-
|
|
662
|
-
if (path.dragctrl) {
|
|
663
|
-
path.moveCtrl(diffX, diffY)
|
|
664
|
-
} else {
|
|
665
|
-
path.movePts(diffX, diffY)
|
|
619
|
+
svgCanvas.replacePathSeg(6, index, [ptX, ptY, lastX, lastY, altX, altY], drawnPath)
|
|
666
620
|
}
|
|
667
621
|
} else {
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
const
|
|
671
|
-
if (
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
622
|
+
const stretchy = getElement('path_stretch_line')
|
|
623
|
+
if (stretchy) {
|
|
624
|
+
const prev = seglist.getItem(index)
|
|
625
|
+
if (prev.pathSegType === 6) {
|
|
626
|
+
const prevX = prev.x + (prev.x - prev.x2)
|
|
627
|
+
const prevY = prev.y + (prev.y - prev.y2)
|
|
628
|
+
svgCanvas.replacePathSeg(
|
|
629
|
+
6,
|
|
630
|
+
1,
|
|
631
|
+
[mouseX, mouseY, prevX * zoom, prevY * zoom, mouseX, mouseY],
|
|
632
|
+
stretchy
|
|
633
|
+
)
|
|
634
|
+
} else if (this.#firstCtrl) {
|
|
635
|
+
svgCanvas.replacePathSeg(6, 1, [mouseX, mouseY, this.#firstCtrl[0], this.#firstCtrl[1], mouseX, mouseY], stretchy)
|
|
636
|
+
} else {
|
|
637
|
+
svgCanvas.replacePathSeg(4, 1, [mouseX, mouseY], stretchy)
|
|
683
638
|
}
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
return
|
|
642
|
+
}
|
|
643
|
+
// if we are dragging a point, let's move it
|
|
644
|
+
if (path.dragging) {
|
|
645
|
+
const pt = svgCanvas.getPointFromGrip({
|
|
646
|
+
x: path.dragging[0],
|
|
647
|
+
y: path.dragging[1]
|
|
648
|
+
}, path)
|
|
649
|
+
const mpt = svgCanvas.getPointFromGrip({
|
|
650
|
+
x: mouseX,
|
|
651
|
+
y: mouseY
|
|
652
|
+
}, path)
|
|
653
|
+
const diffX = mpt.x - pt.x
|
|
654
|
+
const diffY = mpt.y - pt.y
|
|
655
|
+
path.dragging = [mouseX, mouseY]
|
|
656
|
+
|
|
657
|
+
if (path.dragctrl) {
|
|
658
|
+
path.moveCtrl(diffX, diffY)
|
|
659
|
+
} else {
|
|
660
|
+
path.movePts(diffX, diffY)
|
|
661
|
+
}
|
|
662
|
+
} else {
|
|
663
|
+
path.selected_pts = []
|
|
664
|
+
path.eachSeg(function (_i) {
|
|
665
|
+
const seg = this
|
|
666
|
+
if (!seg.next && !seg.prev) return
|
|
667
|
+
|
|
668
|
+
// const {item} = seg;
|
|
669
|
+
const rubberBox = svgCanvas.getRubberBox()
|
|
670
|
+
const rbb = getBBox(rubberBox)
|
|
671
|
+
|
|
672
|
+
const pt = svgCanvas.getGripPt(seg)
|
|
673
|
+
const ptBb = {
|
|
674
|
+
x: pt.x,
|
|
675
|
+
y: pt.y,
|
|
676
|
+
width: 0,
|
|
677
|
+
height: 0
|
|
678
|
+
}
|
|
684
679
|
|
|
685
|
-
|
|
680
|
+
const sel = rectsIntersect(rbb, ptBb)
|
|
686
681
|
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
682
|
+
this.select(sel)
|
|
683
|
+
// Note that addPtsToSelection is not being run
|
|
684
|
+
if (sel) { path.selected_pts.push(seg.index) }
|
|
685
|
+
})
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
/**
|
|
694
690
|
* @typedef module:path.keepElement
|
|
695
691
|
* @type {PlainObject}
|
|
696
692
|
* @property {boolean} keep
|
|
697
693
|
* @property {Element} element
|
|
698
694
|
*/
|
|
699
|
-
|
|
695
|
+
/**
|
|
700
696
|
* @param {Event} evt
|
|
701
697
|
* @param {Element} element
|
|
702
698
|
* @param {Float} _mouseX
|
|
703
699
|
* @param {Float} _mouseY
|
|
704
700
|
* @returns {module:path.keepElement|void}
|
|
705
701
|
*/
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
return {
|
|
718
|
-
keep: true,
|
|
719
|
-
element
|
|
720
|
-
}
|
|
702
|
+
mouseUp (evt, element, _mouseX, _mouseY) {
|
|
703
|
+
const drawnPath = svgCanvas.getDrawnPath()
|
|
704
|
+
// Create mode
|
|
705
|
+
if (svgCanvas.getCurrentMode() === 'path') {
|
|
706
|
+
this.#newPoint = null
|
|
707
|
+
if (!drawnPath) {
|
|
708
|
+
element = getElement(svgCanvas.getId())
|
|
709
|
+
svgCanvas.setStarted(false)
|
|
710
|
+
this.#firstCtrl = null
|
|
721
711
|
}
|
|
722
712
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
713
|
+
return {
|
|
714
|
+
keep: true,
|
|
715
|
+
element
|
|
716
|
+
}
|
|
717
|
+
}
|
|
727
718
|
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
719
|
+
// Edit mode
|
|
720
|
+
const rubberBox = svgCanvas.getRubberBox()
|
|
721
|
+
if (path.dragging) {
|
|
722
|
+
const lastPt = path.cur_pt
|
|
731
723
|
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
724
|
+
path.dragging = false
|
|
725
|
+
path.dragctrl = false
|
|
726
|
+
path.update()
|
|
735
727
|
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
} else if (rubberBox?.getAttribute('display') !== 'none') {
|
|
740
|
-
// Done with multi-node-select
|
|
741
|
-
rubberBox.setAttribute('display', 'none')
|
|
728
|
+
if (this.#hasMoved) {
|
|
729
|
+
path.endChanges('Move path point(s)')
|
|
730
|
+
}
|
|
742
731
|
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
732
|
+
if (!evt.shiftKey && !this.#hasMoved) {
|
|
733
|
+
path.selectPt(lastPt)
|
|
734
|
+
}
|
|
735
|
+
} else if (rubberBox?.getAttribute('display') !== 'none') {
|
|
736
|
+
// Done with multi-node-select
|
|
737
|
+
rubberBox.setAttribute('display', 'none')
|
|
746
738
|
|
|
747
|
-
|
|
748
|
-
} else {
|
|
739
|
+
if (rubberBox.getAttribute('width') <= 2 && rubberBox.getAttribute('height') <= 2) {
|
|
749
740
|
pathActionsMethod.toSelectMode(evt.target)
|
|
750
741
|
}
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
}
|
|
754
|
-
|
|
742
|
+
|
|
743
|
+
// else, move back to select mode
|
|
744
|
+
} else {
|
|
745
|
+
pathActionsMethod.toSelectMode(evt.target)
|
|
746
|
+
}
|
|
747
|
+
this.#hasMoved = false
|
|
748
|
+
return undefined
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
/**
|
|
755
752
|
* @param {Element} element
|
|
756
753
|
* @returns {void}
|
|
757
754
|
*/
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
755
|
+
toEditMode (element) {
|
|
756
|
+
path = svgCanvas.getPath_(element)
|
|
757
|
+
svgCanvas.setCurrentMode('pathedit')
|
|
758
|
+
svgCanvas.clearSelection()
|
|
759
|
+
path.setPathContext()
|
|
760
|
+
path.show(true).update()
|
|
761
|
+
path.oldbbox = getBBox(path.elem)
|
|
762
|
+
this.#subpath = false
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
/**
|
|
768
766
|
* @param {Element} elem
|
|
769
767
|
* @fires module:svgcanvas.SvgCanvas#event:selected
|
|
770
768
|
* @returns {void}
|
|
771
769
|
*/
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
770
|
+
toSelectMode (elem) {
|
|
771
|
+
const selPath = (elem === path.elem)
|
|
772
|
+
svgCanvas.setCurrentMode('select')
|
|
773
|
+
path.setPathContext()
|
|
774
|
+
path.show(false)
|
|
775
|
+
this.#currentPath = false
|
|
776
|
+
svgCanvas.clearSelection()
|
|
777
|
+
|
|
778
|
+
if (path.matrix) {
|
|
779
|
+
// Rotated, so may need to re-calculate the center
|
|
780
|
+
svgCanvas.recalcRotatedPath()
|
|
781
|
+
}
|
|
784
782
|
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
783
|
+
if (selPath) {
|
|
784
|
+
svgCanvas.call('selected', [elem])
|
|
785
|
+
svgCanvas.addToSelection([elem], true)
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
/**
|
|
791
790
|
* @param {boolean} on
|
|
792
791
|
* @returns {void}
|
|
793
792
|
*/
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
793
|
+
addSubPath (on) {
|
|
794
|
+
if (on) {
|
|
795
|
+
// Internally we go into "path" mode, but in the UI it will
|
|
796
|
+
// still appear as if in "pathedit" mode.
|
|
797
|
+
svgCanvas.setCurrentMode('path')
|
|
798
|
+
this.#subpath = true
|
|
799
|
+
} else {
|
|
800
|
+
pathActionsMethod.clear(true)
|
|
801
|
+
pathActionsMethod.toEditMode(path.elem)
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
/**
|
|
806
806
|
* @param {Element} target
|
|
807
807
|
* @returns {void}
|
|
808
808
|
*/
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
809
|
+
select (target) {
|
|
810
|
+
if (this.#currentPath === target) {
|
|
811
|
+
pathActionsMethod.toEditMode(target)
|
|
812
|
+
svgCanvas.setCurrentMode('pathedit')
|
|
813
813
|
// going into pathedit mode
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
814
|
+
} else {
|
|
815
|
+
this.#currentPath = target
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
/**
|
|
819
820
|
* @fires module:svgcanvas.SvgCanvas#event:changed
|
|
820
821
|
* @returns {void}
|
|
821
822
|
*/
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
823
|
+
reorient () {
|
|
824
|
+
const elem = svgCanvas.getSelectedElements()[0]
|
|
825
|
+
if (!elem) { return }
|
|
826
|
+
if (elem.nodeName !== 'path') { return }
|
|
827
|
+
const angl = getRotationAngle(elem)
|
|
828
|
+
if (angl === 0) { return }
|
|
829
|
+
|
|
830
|
+
const batchCmd = new BatchCommand('Reorient path')
|
|
831
|
+
const changes = {
|
|
832
|
+
d: elem.getAttribute('d'),
|
|
833
|
+
transform: elem.getAttribute('transform')
|
|
834
|
+
}
|
|
835
|
+
batchCmd.addSubCommand(new ChangeElementCommand(elem, changes))
|
|
836
|
+
svgCanvas.clearSelection()
|
|
837
|
+
this.resetOrientation(elem)
|
|
836
838
|
|
|
837
|
-
|
|
839
|
+
svgCanvas.addCommandToHistory(batchCmd)
|
|
838
840
|
|
|
839
|
-
|
|
840
|
-
|
|
841
|
+
// Set matrix to null
|
|
842
|
+
svgCanvas.getPath_(elem).show(false).matrix = null
|
|
841
843
|
|
|
842
|
-
|
|
844
|
+
this.clear()
|
|
843
845
|
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
846
|
+
svgCanvas.addToSelection([elem], true)
|
|
847
|
+
svgCanvas.call('changed', svgCanvas.getSelectedElements())
|
|
848
|
+
}
|
|
847
849
|
|
|
848
|
-
|
|
850
|
+
/**
|
|
849
851
|
* @param {boolean} remove Not in use
|
|
850
852
|
* @returns {void}
|
|
851
853
|
*/
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
})
|
|
865
|
-
firstCtrl = null
|
|
866
|
-
svgCanvas.setDrawnPath(null)
|
|
867
|
-
svgCanvas.setStarted(false)
|
|
868
|
-
} else if (svgCanvas.getCurrentMode() === 'pathedit') {
|
|
869
|
-
this.toSelectMode()
|
|
854
|
+
clear () {
|
|
855
|
+
const drawnPath = svgCanvas.getDrawnPath()
|
|
856
|
+
this.#currentPath = null
|
|
857
|
+
if (drawnPath) {
|
|
858
|
+
const elem = getElement(svgCanvas.getId())
|
|
859
|
+
const psl = getElement('path_stretch_line')
|
|
860
|
+
psl.parentNode.removeChild(psl)
|
|
861
|
+
elem.parentNode.removeChild(elem)
|
|
862
|
+
const pathpointgripContainer = getElement('pathpointgrip_container')
|
|
863
|
+
const elements = pathpointgripContainer.querySelectorAll('*')
|
|
864
|
+
for (const el of elements) {
|
|
865
|
+
el.setAttribute('display', 'none')
|
|
870
866
|
}
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
867
|
+
this.#firstCtrl = null
|
|
868
|
+
svgCanvas.setDrawnPath(null)
|
|
869
|
+
svgCanvas.setStarted(false)
|
|
870
|
+
} else if (svgCanvas.getCurrentMode() === 'pathedit') {
|
|
871
|
+
this.toSelectMode()
|
|
872
|
+
}
|
|
873
|
+
if (path) { path.init().show(false) }
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
/**
|
|
874
877
|
* @param {?(Element|SVGPathElement)} pth
|
|
875
878
|
* @returns {false|void}
|
|
876
879
|
*/
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
}
|
|
910
|
-
svgCanvas.replacePathSeg(type, i, pts, pth)
|
|
880
|
+
resetOrientation (pth) {
|
|
881
|
+
if (pth?.nodeName !== 'path') { return false }
|
|
882
|
+
const tlist = getTransformList(pth)
|
|
883
|
+
const m = transformListToTransform(tlist).matrix
|
|
884
|
+
tlist.clear()
|
|
885
|
+
pth.removeAttribute('transform')
|
|
886
|
+
const segList = pth.pathSegList
|
|
887
|
+
|
|
888
|
+
// Opera/win/non-EN throws an error here.
|
|
889
|
+
// TODO: Find out why!
|
|
890
|
+
// Presumed fixed in Opera 10.5, so commented out for now
|
|
891
|
+
|
|
892
|
+
// try {
|
|
893
|
+
const len = segList.numberOfItems
|
|
894
|
+
// } catch(err) {
|
|
895
|
+
// const fixed_d = pathActions.convertPath(pth);
|
|
896
|
+
// pth.setAttribute('d', fixed_d);
|
|
897
|
+
// segList = pth.pathSegList;
|
|
898
|
+
// const len = segList.numberOfItems;
|
|
899
|
+
// }
|
|
900
|
+
// let lastX, lastY;
|
|
901
|
+
for (let i = 0; i < len; ++i) {
|
|
902
|
+
const seg = segList.getItem(i)
|
|
903
|
+
const type = seg.pathSegType
|
|
904
|
+
if (type === 1) { continue }
|
|
905
|
+
const pts = []
|
|
906
|
+
for (const n of ['', 1, 2]) {
|
|
907
|
+
const x = seg['x' + n]
|
|
908
|
+
const y = seg['y' + n]
|
|
909
|
+
if (x !== undefined && y !== undefined) {
|
|
910
|
+
const pt = transformPoint(x, y, m)
|
|
911
|
+
pts.push(pt.x, pt.y)
|
|
912
|
+
}
|
|
911
913
|
}
|
|
914
|
+
svgCanvas.replacePathSeg(type, i, pts, pth)
|
|
915
|
+
}
|
|
912
916
|
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
+
svgCanvas.reorientGrads(pth, m)
|
|
918
|
+
return undefined
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
/**
|
|
917
922
|
* @returns {void}
|
|
918
923
|
*/
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
924
|
+
zoomChange () {
|
|
925
|
+
if (svgCanvas.getCurrentMode() === 'pathedit') {
|
|
926
|
+
path.update()
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
/**
|
|
925
931
|
* @typedef {PlainObject} module:path.NodePoint
|
|
926
932
|
* @property {Float} x
|
|
927
933
|
* @property {Float} y
|
|
928
934
|
* @property {Integer} type
|
|
929
935
|
*/
|
|
930
|
-
|
|
936
|
+
/**
|
|
931
937
|
* @returns {module:path.NodePoint}
|
|
932
938
|
*/
|
|
933
|
-
|
|
934
|
-
|
|
939
|
+
getNodePoint () {
|
|
940
|
+
const selPt = path.selected_pts.length ? path.selected_pts[0] : 1
|
|
941
|
+
|
|
942
|
+
const seg = path.segs[selPt]
|
|
943
|
+
return {
|
|
944
|
+
x: seg.item.x,
|
|
945
|
+
y: seg.item.y,
|
|
946
|
+
type: seg.type
|
|
947
|
+
}
|
|
948
|
+
}
|
|
935
949
|
|
|
936
|
-
|
|
937
|
-
return {
|
|
938
|
-
x: seg.item.x,
|
|
939
|
-
y: seg.item.y,
|
|
940
|
-
type: seg.type
|
|
941
|
-
}
|
|
942
|
-
},
|
|
943
|
-
/**
|
|
950
|
+
/**
|
|
944
951
|
* @param {boolean} linkPoints
|
|
945
952
|
* @returns {void}
|
|
946
953
|
*/
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
954
|
+
linkControlPoints (linkPoints) {
|
|
955
|
+
svgCanvas.setLinkControlPoints(linkPoints)
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
/**
|
|
951
959
|
* @returns {void}
|
|
952
960
|
*/
|
|
953
|
-
|
|
954
|
-
|
|
961
|
+
clonePathNode () {
|
|
962
|
+
path.storeD()
|
|
955
963
|
|
|
956
|
-
|
|
957
|
-
|
|
964
|
+
const selPts = path.selected_pts
|
|
965
|
+
// const {segs} = path;
|
|
958
966
|
|
|
959
|
-
|
|
960
|
-
|
|
967
|
+
let i = selPts.length
|
|
968
|
+
const nums = []
|
|
961
969
|
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
970
|
+
while (i--) {
|
|
971
|
+
const pt = selPts[i]
|
|
972
|
+
path.addSeg(pt)
|
|
965
973
|
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
974
|
+
nums.push(pt + i)
|
|
975
|
+
nums.push(pt + i + 1)
|
|
976
|
+
}
|
|
977
|
+
path.init().addPtsToSelection(nums)
|
|
978
|
+
|
|
979
|
+
path.endChanges('Clone path node(s)')
|
|
980
|
+
}
|
|
970
981
|
|
|
971
|
-
|
|
972
|
-
},
|
|
973
|
-
/**
|
|
982
|
+
/**
|
|
974
983
|
* @returns {void}
|
|
975
984
|
*/
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
985
|
+
opencloseSubPath () {
|
|
986
|
+
const selPts = path.selected_pts
|
|
987
|
+
// Only allow one selected node for now
|
|
988
|
+
if (selPts.length !== 1) { return }
|
|
980
989
|
|
|
981
|
-
|
|
982
|
-
|
|
990
|
+
const { elem } = path
|
|
991
|
+
const list = elem.pathSegList
|
|
983
992
|
|
|
984
|
-
|
|
993
|
+
// const len = list.numberOfItems;
|
|
985
994
|
|
|
986
|
-
|
|
995
|
+
const index = selPts[0]
|
|
987
996
|
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
// Check if subpath is already open
|
|
992
|
-
path.eachSeg(function (i) {
|
|
993
|
-
if (this.type === 2 && i <= index) {
|
|
994
|
-
startItem = this.item
|
|
995
|
-
}
|
|
996
|
-
if (i <= index) { return true }
|
|
997
|
-
if (this.type === 2) {
|
|
998
|
-
// Found M first, so open
|
|
999
|
-
openPt = i
|
|
1000
|
-
return false
|
|
1001
|
-
}
|
|
1002
|
-
if (this.type === 1) {
|
|
1003
|
-
// Found Z first, so closed
|
|
1004
|
-
openPt = false
|
|
1005
|
-
return false
|
|
1006
|
-
}
|
|
1007
|
-
return true
|
|
1008
|
-
})
|
|
997
|
+
let openPt = null
|
|
998
|
+
let startItem = null
|
|
1009
999
|
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1000
|
+
// Check if subpath is already open
|
|
1001
|
+
path.eachSeg(function (i) {
|
|
1002
|
+
if (this.type === 2 && i <= index) {
|
|
1003
|
+
startItem = this.item
|
|
1004
|
+
}
|
|
1005
|
+
if (i <= index) return true
|
|
1006
|
+
if (this.type === 2) {
|
|
1007
|
+
// Found M first, so open
|
|
1008
|
+
openPt = i
|
|
1009
|
+
return false
|
|
1010
|
+
}
|
|
1011
|
+
if (this.type === 1) {
|
|
1012
|
+
// Found Z first, so closed
|
|
1013
|
+
openPt = false
|
|
1014
|
+
return false
|
|
1013
1015
|
}
|
|
1016
|
+
return true
|
|
1017
|
+
})
|
|
1014
1018
|
|
|
1015
|
-
|
|
1016
|
-
|
|
1019
|
+
if (!openPt) {
|
|
1020
|
+
// Single path, so close last seg
|
|
1021
|
+
openPt = path.segs.length - 1
|
|
1022
|
+
}
|
|
1017
1023
|
|
|
1018
|
-
|
|
1019
|
-
|
|
1024
|
+
if (openPt !== false) {
|
|
1025
|
+
// Close this path
|
|
1020
1026
|
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
list.appendItem(newseg)
|
|
1024
|
-
list.appendItem(closer)
|
|
1025
|
-
} else {
|
|
1026
|
-
list.insertItemBefore(closer, openPt)
|
|
1027
|
-
list.insertItemBefore(newseg, openPt)
|
|
1028
|
-
}
|
|
1027
|
+
// Create a line going to the previous "M"
|
|
1028
|
+
const newseg = elem.createSVGPathSegLinetoAbs(startItem.x, startItem.y)
|
|
1029
1029
|
|
|
1030
|
-
|
|
1031
|
-
|
|
1030
|
+
const closer = elem.createSVGPathSegClosePath()
|
|
1031
|
+
if (openPt === path.segs.length - 1) {
|
|
1032
|
+
list.appendItem(newseg)
|
|
1033
|
+
list.appendItem(closer)
|
|
1034
|
+
} else {
|
|
1035
|
+
list.insertItemBefore(closer, openPt)
|
|
1036
|
+
list.insertItemBefore(newseg, openPt)
|
|
1032
1037
|
}
|
|
1033
1038
|
|
|
1034
|
-
|
|
1035
|
-
|
|
1039
|
+
path.init().selectPt(openPt + 1)
|
|
1040
|
+
return
|
|
1041
|
+
}
|
|
1036
1042
|
|
|
1037
|
-
|
|
1038
|
-
|
|
1043
|
+
// M 1,1 L 2,2 L 3,3 L 1,1 z // open at 2,2
|
|
1044
|
+
// M 2,2 L 3,3 L 1,1
|
|
1039
1045
|
|
|
1040
|
-
|
|
1046
|
+
// M 1,1 L 2,2 L 1,1 z M 4,4 L 5,5 L6,6 L 5,5 z
|
|
1047
|
+
// M 1,1 L 2,2 L 1,1 z [M 4,4] L 5,5 L(M)6,6 L 5,5 z
|
|
1041
1048
|
|
|
1042
|
-
|
|
1043
|
-
list.removeItem(index) // Removes last "L"
|
|
1044
|
-
list.removeItem(index) // Removes the "Z"
|
|
1045
|
-
path.init().selectPt(index - 1)
|
|
1046
|
-
return
|
|
1047
|
-
}
|
|
1049
|
+
const seg = path.segs[index]
|
|
1048
1050
|
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
//
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1051
|
+
if (seg.mate) {
|
|
1052
|
+
list.removeItem(index) // Removes last "L"
|
|
1053
|
+
list.removeItem(index) // Removes the "Z"
|
|
1054
|
+
path.init().selectPt(index - 1)
|
|
1055
|
+
return
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
let lastM; let zSeg
|
|
1059
|
+
|
|
1060
|
+
// Find this sub-path's closing point and remove
|
|
1061
|
+
for (let i = 0; i < list.numberOfItems; i++) {
|
|
1062
|
+
const item = list.getItem(i)
|
|
1063
|
+
|
|
1064
|
+
if (item.pathSegType === 2) {
|
|
1065
|
+
// Find the preceding M
|
|
1066
|
+
lastM = i
|
|
1067
|
+
} else if (i === index) {
|
|
1068
|
+
// Remove it
|
|
1069
|
+
list.removeItem(lastM)
|
|
1070
|
+
// index--;
|
|
1071
|
+
} else if (item.pathSegType === 1 && index < i) {
|
|
1072
|
+
// Remove the closing seg of this subpath
|
|
1073
|
+
zSeg = i - 1
|
|
1074
|
+
list.removeItem(i)
|
|
1075
|
+
break
|
|
1068
1076
|
}
|
|
1077
|
+
}
|
|
1069
1078
|
|
|
1070
|
-
|
|
1079
|
+
let num = (index - lastM) - 1
|
|
1071
1080
|
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1081
|
+
while (num--) {
|
|
1082
|
+
list.insertItemBefore(list.getItem(lastM), zSeg)
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
const pt = list.getItem(lastM)
|
|
1075
1086
|
|
|
1076
|
-
|
|
1087
|
+
// Make this point the new "M"
|
|
1088
|
+
svgCanvas.replacePathSeg(2, lastM, [pt.x, pt.y])
|
|
1077
1089
|
|
|
1078
|
-
|
|
1079
|
-
svgCanvas.replacePathSeg(2, lastM, [pt.x, pt.y])
|
|
1090
|
+
// i = index; // i is local here, so has no effect; what was the intent for this?
|
|
1080
1091
|
|
|
1081
|
-
|
|
1092
|
+
path.init().selectPt(0)
|
|
1093
|
+
}
|
|
1082
1094
|
|
|
1083
|
-
|
|
1084
|
-
},
|
|
1085
|
-
/**
|
|
1095
|
+
/**
|
|
1086
1096
|
* @returns {void}
|
|
1087
1097
|
*/
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1098
|
+
deletePathNode () {
|
|
1099
|
+
if (!pathActionsMethod.canDeleteNodes) { return }
|
|
1100
|
+
path.storeD()
|
|
1091
1101
|
|
|
1092
|
-
|
|
1102
|
+
const selPts = path.selected_pts
|
|
1093
1103
|
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1104
|
+
let i = selPts.length
|
|
1105
|
+
while (i--) {
|
|
1106
|
+
const pt = selPts[i]
|
|
1107
|
+
path.deleteSeg(pt)
|
|
1108
|
+
}
|
|
1099
1109
|
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1110
|
+
// Cleanup
|
|
1111
|
+
const cleanup = () => {
|
|
1112
|
+
const segList = path.elem.pathSegList
|
|
1113
|
+
let len = segList.numberOfItems
|
|
1104
1114
|
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
}
|
|
1115
|
+
const remItems = (pos, count) => {
|
|
1116
|
+
while (count--) {
|
|
1117
|
+
segList.removeItem(pos)
|
|
1109
1118
|
}
|
|
1119
|
+
}
|
|
1110
1120
|
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
}
|
|
1121
|
+
if (len <= 1) { return true }
|
|
1122
|
+
|
|
1123
|
+
while (len--) {
|
|
1124
|
+
const item = segList.getItem(len)
|
|
1125
|
+
if (item.pathSegType === 1) {
|
|
1126
|
+
const prev = segList.getItem(len - 1)
|
|
1127
|
+
const nprev = segList.getItem(len - 2)
|
|
1128
|
+
if (prev.pathSegType === 2) {
|
|
1129
|
+
remItems(len - 1, 2)
|
|
1130
|
+
cleanup()
|
|
1131
|
+
break
|
|
1132
|
+
} else if (nprev.pathSegType === 2) {
|
|
1133
|
+
remItems(len - 2, 3)
|
|
1134
|
+
cleanup()
|
|
1135
|
+
break
|
|
1136
|
+
}
|
|
1137
|
+
} else if (item.pathSegType === 2 && len > 0) {
|
|
1138
|
+
const prevType = segList.getItem(len - 1).pathSegType
|
|
1139
|
+
// Path has M M
|
|
1140
|
+
if (prevType === 2) {
|
|
1141
|
+
remItems(len - 1, 1)
|
|
1142
|
+
cleanup()
|
|
1143
|
+
break
|
|
1144
|
+
// Entire path ends with Z M
|
|
1145
|
+
} else if (prevType === 1 && segList.numberOfItems - 1 === len) {
|
|
1146
|
+
remItems(len, 1)
|
|
1147
|
+
cleanup()
|
|
1148
|
+
break
|
|
1140
1149
|
}
|
|
1141
1150
|
}
|
|
1142
|
-
return false
|
|
1143
1151
|
}
|
|
1152
|
+
return false
|
|
1153
|
+
}
|
|
1144
1154
|
|
|
1145
|
-
|
|
1155
|
+
cleanup()
|
|
1146
1156
|
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1157
|
+
// Completely delete a path with 1 or 0 segments
|
|
1158
|
+
if (path.elem.pathSegList.numberOfItems <= 1) {
|
|
1159
|
+
pathActionsMethod.toSelectMode(path.elem)
|
|
1160
|
+
svgCanvas.canvas.deleteSelectedElements()
|
|
1161
|
+
return
|
|
1162
|
+
}
|
|
1153
1163
|
|
|
1154
|
-
|
|
1155
|
-
|
|
1164
|
+
path.init()
|
|
1165
|
+
path.clearSelection()
|
|
1156
1166
|
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
// Can't seem to use `@borrows` here, so using `@see`
|
|
1165
|
-
/**
|
|
1166
|
-
* Smooth polyline into path.
|
|
1167
|
-
* @function module:path.pathActions.smoothPolylineIntoPath
|
|
1168
|
-
* @see module:path~smoothPolylineIntoPath
|
|
1169
|
-
*/
|
|
1170
|
-
smoothPolylineIntoPath,
|
|
1171
|
-
/* eslint-enable */
|
|
1172
|
-
/**
|
|
1173
|
-
* @param {?Integer} v See {@link https://www.w3.org/TR/SVG/single-page.html#paths-InterfaceSVGPathSeg}
|
|
1174
|
-
* @returns {void}
|
|
1175
|
-
*/
|
|
1176
|
-
setSegType (v) {
|
|
1177
|
-
path?.setSegType(v)
|
|
1178
|
-
},
|
|
1179
|
-
/**
|
|
1180
|
-
* @param {string} attr
|
|
1181
|
-
* @param {Float} newValue
|
|
1182
|
-
* @returns {void}
|
|
1183
|
-
*/
|
|
1184
|
-
moveNode (attr, newValue) {
|
|
1185
|
-
const selPts = path.selected_pts
|
|
1186
|
-
if (!selPts.length) { return }
|
|
1167
|
+
// TODO: Find right way to select point now
|
|
1168
|
+
// path.selectPt(selPt);
|
|
1169
|
+
if (window.opera) { // Opera repaints incorrectly
|
|
1170
|
+
path.elem.setAttribute('d', path.elem.getAttribute('d'))
|
|
1171
|
+
}
|
|
1172
|
+
path.endChanges('Delete path node(s)')
|
|
1173
|
+
}
|
|
1187
1174
|
|
|
1188
|
-
|
|
1175
|
+
// Can't seem to use `@borrows` here, so using `@see`
|
|
1176
|
+
/**
|
|
1177
|
+
* Smooth polyline into path.
|
|
1178
|
+
* @function module:path.pathActions.smoothPolylineIntoPath
|
|
1179
|
+
* @see module:path~smoothPolylineIntoPath
|
|
1180
|
+
*/
|
|
1181
|
+
smoothPolylineIntoPath (element) {
|
|
1182
|
+
return this.#smoothPolylineIntoPath(element)
|
|
1183
|
+
}
|
|
1189
1184
|
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1185
|
+
/* eslint-enable */
|
|
1186
|
+
/**
|
|
1187
|
+
* @param {?Integer} v See {@link https://www.w3.org/TR/SVG/single-page.html#paths-InterfaceSVGPathSeg}
|
|
1188
|
+
* @returns {void}
|
|
1189
|
+
*/
|
|
1190
|
+
setSegType (v) {
|
|
1191
|
+
path?.setSegType(v)
|
|
1192
|
+
}
|
|
1194
1193
|
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
// Adds an extra segment if the last seg before a Z doesn't end
|
|
1204
|
-
// at its M point
|
|
1205
|
-
// M0,0 L0,100 L100,100 z
|
|
1206
|
-
const segList = elem.pathSegList
|
|
1207
|
-
const len = segList.numberOfItems
|
|
1208
|
-
let lastM
|
|
1209
|
-
for (let i = 0; i < len; ++i) {
|
|
1210
|
-
const item = segList.getItem(i)
|
|
1211
|
-
if (item.pathSegType === 2) { // 2 => M segment type (move to)
|
|
1212
|
-
lastM = item
|
|
1213
|
-
}
|
|
1194
|
+
/**
|
|
1195
|
+
* @param {string} attr
|
|
1196
|
+
* @param {Float} newValue
|
|
1197
|
+
* @returns {void}
|
|
1198
|
+
*/
|
|
1199
|
+
moveNode (attr, newValue) {
|
|
1200
|
+
const selPts = path.selected_pts
|
|
1201
|
+
if (!selPts.length) { return }
|
|
1214
1202
|
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1203
|
+
path.storeD()
|
|
1204
|
+
|
|
1205
|
+
// Get first selected point
|
|
1206
|
+
const seg = path.segs[selPts[0]]
|
|
1207
|
+
const diff = { x: 0, y: 0 }
|
|
1208
|
+
diff[attr] = newValue - seg.item[attr]
|
|
1209
|
+
|
|
1210
|
+
seg.move(diff.x, diff.y)
|
|
1211
|
+
path.endChanges('Move path point')
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
/**
|
|
1215
|
+
* @param {Element} elem
|
|
1216
|
+
* @returns {void}
|
|
1217
|
+
*/
|
|
1218
|
+
fixEnd (elem) {
|
|
1219
|
+
// Adds an extra segment if the last seg before a Z doesn't end
|
|
1220
|
+
// at its M point
|
|
1221
|
+
// M0,0 L0,100 L100,100 z
|
|
1222
|
+
const segList = elem.pathSegList
|
|
1223
|
+
const len = segList.numberOfItems
|
|
1224
|
+
let lastM
|
|
1225
|
+
for (let i = 0; i < len; ++i) {
|
|
1226
|
+
const item = segList.getItem(i)
|
|
1227
|
+
if (item.pathSegType === 2) { // 2 => M segment type (move to)
|
|
1228
|
+
lastM = item
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
if (item.pathSegType === 1) { // 1 => Z segment type (close path)
|
|
1232
|
+
const prev = segList.getItem(i - 1)
|
|
1233
|
+
if (prev.x !== lastM.x || prev.y !== lastM.y) {
|
|
1234
|
+
// Add an L segment here
|
|
1235
|
+
const newseg = elem.createSVGPathSegLinetoAbs(lastM.x, lastM.y)
|
|
1236
|
+
segList.insertItemBefore(newseg, i)
|
|
1237
|
+
// Can this be done better?
|
|
1238
|
+
pathActionsMethod.fixEnd(elem)
|
|
1239
|
+
break
|
|
1225
1240
|
}
|
|
1226
1241
|
}
|
|
1227
|
-
}
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
// Can't seem to use `@borrows` here, so using `@see`
|
|
1246
|
+
/**
|
|
1247
|
+
* Convert a path to one with only absolute or relative values.
|
|
1248
|
+
* @function module:path.pathActions.convertPath
|
|
1249
|
+
* @see module:path.convertPath
|
|
1250
|
+
*/
|
|
1251
|
+
convertPath (pth, toRel) {
|
|
1252
|
+
return convertPath(pth, toRel)
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
// Export singleton instance for backward compatibility
|
|
1257
|
+
export const pathActionsMethod = new PathActions()
|
|
1237
1258
|
// end pathActions
|