* fix release script * fix svgcanvas edge cases * Update path-actions.js * add modern js * update deps * Update CHANGES.md
512 lines
18 KiB
JavaScript
512 lines
18 KiB
JavaScript
/* globals SVGPathSeg */
|
|
import 'pathseg'
|
|
import { NS } from '../../packages/svgcanvas/core/namespaces.js'
|
|
import * as utilities from '../../packages/svgcanvas/core/utilities.js'
|
|
import { convertPath as convertPathActions } from '../../packages/svgcanvas/core/path-actions.js'
|
|
import * as pathModule from '../../packages/svgcanvas/core/path.js'
|
|
import { Path, Segment } from '../../packages/svgcanvas/core/path-method.js'
|
|
import { init as unitsInit } from '../../packages/svgcanvas/core/units.js'
|
|
|
|
describe('path', function () {
|
|
/**
|
|
* @typedef {GenericArray} EditorContexts
|
|
* @property {module:path.EditorContext} 0
|
|
* @property {module:path.EditorContext} 1
|
|
*/
|
|
|
|
/**
|
|
* @param {SVGSVGElement} [svg]
|
|
* @returns {EditorContexts}
|
|
*/
|
|
function getMockContexts (svg) {
|
|
svg = svg || document.createElementNS(NS.SVG, 'svg')
|
|
const selectorParentGroup = document.createElementNS(NS.SVG, 'g')
|
|
selectorParentGroup.setAttribute('id', 'selectorParentGroup')
|
|
svg.append(selectorParentGroup)
|
|
return [
|
|
/**
|
|
* @implements {module:path.EditorContext}
|
|
*/
|
|
{
|
|
getSvgRoot () { return svg },
|
|
getZoom () { return 1 }
|
|
},
|
|
/**
|
|
* @implements {module:utilities.EditorContext}
|
|
*/
|
|
{
|
|
getDOMDocument () { return svg },
|
|
getDOMContainer () { return svg },
|
|
getSvgRoot () { return svg }
|
|
}
|
|
]
|
|
}
|
|
|
|
it('Test svgedit.path.init exposes recalcRotatedPath', function () {
|
|
const [mockPathContext] = getMockContexts()
|
|
pathModule.init(mockPathContext)
|
|
assert.equal(typeof mockPathContext.recalcRotatedPath, 'function')
|
|
})
|
|
|
|
it('Test svgedit.path.replacePathSeg', function () {
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M0,0 L10,11 L20,21Z')
|
|
|
|
const [mockPathContext, mockUtilitiesContext] = getMockContexts()
|
|
pathModule.init(mockPathContext)
|
|
utilities.init(mockUtilitiesContext)
|
|
new Path(path) // eslint-disable-line no-new
|
|
|
|
assert.equal(path.pathSegList.getItem(1).pathSegTypeAsLetter, 'L')
|
|
assert.equal(path.pathSegList.getItem(1).x, 10)
|
|
assert.equal(path.pathSegList.getItem(1).y, 11)
|
|
|
|
pathModule.replacePathSeg(SVGPathSeg.PATHSEG_LINETO_REL, 1, [30, 31], path)
|
|
|
|
assert.equal(path.pathSegList.getItem(1).pathSegTypeAsLetter, 'l')
|
|
assert.equal(path.pathSegList.getItem(1).x, 30)
|
|
assert.equal(path.pathSegList.getItem(1).y, 31)
|
|
})
|
|
|
|
it('Test svgedit.path.Segment.setType simple', function () {
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M0,0 L10,11 L20,21Z')
|
|
|
|
const [mockPathContext, mockUtilitiesContext] = getMockContexts()
|
|
pathModule.init(mockPathContext)
|
|
utilities.init(mockUtilitiesContext)
|
|
new Path(path) // eslint-disable-line no-new
|
|
|
|
assert.equal(path.pathSegList.getItem(1).pathSegTypeAsLetter, 'L')
|
|
assert.equal(path.pathSegList.getItem(1).x, 10)
|
|
assert.equal(path.pathSegList.getItem(1).y, 11)
|
|
|
|
const segment = new Segment(1, path.pathSegList.getItem(1))
|
|
segment.setType(SVGPathSeg.PATHSEG_LINETO_REL, [30, 31])
|
|
assert.equal(segment.item.pathSegTypeAsLetter, 'l')
|
|
assert.equal(segment.item.x, 30)
|
|
assert.equal(segment.item.y, 31)
|
|
|
|
// Also verify that the actual path changed.
|
|
assert.equal(path.pathSegList.getItem(1).pathSegTypeAsLetter, 'l')
|
|
assert.equal(path.pathSegList.getItem(1).x, 30)
|
|
assert.equal(path.pathSegList.getItem(1).y, 31)
|
|
})
|
|
|
|
it('Test svgedit.path.Segment.setType with control points', function () {
|
|
// Setup the dom for a mock control group.
|
|
const svg = document.createElementNS(NS.SVG, 'svg')
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M0,0 C11,12 13,14 15,16 Z')
|
|
svg.append(path)
|
|
|
|
const [mockPathContext, mockUtilitiesContext] = getMockContexts(svg)
|
|
pathModule.init(mockPathContext)
|
|
utilities.init(mockUtilitiesContext)
|
|
const segment = new Segment(1, path.pathSegList.getItem(1))
|
|
segment.path = new Path(path)
|
|
|
|
assert.equal(path.pathSegList.getItem(1).pathSegTypeAsLetter, 'C')
|
|
assert.equal(path.pathSegList.getItem(1).x1, 11)
|
|
assert.equal(path.pathSegList.getItem(1).y1, 12)
|
|
assert.equal(path.pathSegList.getItem(1).x2, 13)
|
|
assert.equal(path.pathSegList.getItem(1).y2, 14)
|
|
assert.equal(path.pathSegList.getItem(1).x, 15)
|
|
assert.equal(path.pathSegList.getItem(1).y, 16)
|
|
|
|
segment.setType(SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL, [30, 31, 32, 33, 34, 35])
|
|
assert.equal(path.pathSegList.getItem(1).pathSegTypeAsLetter, 'c')
|
|
assert.equal(path.pathSegList.getItem(1).x1, 32)
|
|
assert.equal(path.pathSegList.getItem(1).y1, 33)
|
|
assert.equal(path.pathSegList.getItem(1).x2, 34)
|
|
assert.equal(path.pathSegList.getItem(1).y2, 35)
|
|
assert.equal(path.pathSegList.getItem(1).x, 30)
|
|
assert.equal(path.pathSegList.getItem(1).y, 31)
|
|
})
|
|
|
|
it('Test svgedit.path.Segment.move', function () {
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M0,0 L10,11 L20,21Z')
|
|
|
|
const [mockPathContext, mockUtilitiesContext] = getMockContexts()
|
|
pathModule.init(mockPathContext)
|
|
utilities.init(mockUtilitiesContext)
|
|
new Path(path) // eslint-disable-line no-new
|
|
|
|
assert.equal(path.pathSegList.getItem(1).pathSegTypeAsLetter, 'L')
|
|
assert.equal(path.pathSegList.getItem(1).x, 10)
|
|
assert.equal(path.pathSegList.getItem(1).y, 11)
|
|
|
|
const segment = new Segment(1, path.pathSegList.getItem(1))
|
|
segment.move(-3, 4)
|
|
assert.equal(path.pathSegList.getItem(1).pathSegTypeAsLetter, 'L')
|
|
assert.equal(path.pathSegList.getItem(1).x, 7)
|
|
assert.equal(path.pathSegList.getItem(1).y, 15)
|
|
})
|
|
|
|
it('Test svgedit.path.Segment.move for quadratic curve', function () {
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M0,0 Q11,12 15,16')
|
|
|
|
const [mockPathContext, mockUtilitiesContext] = getMockContexts()
|
|
pathModule.init(mockPathContext)
|
|
utilities.init(mockUtilitiesContext)
|
|
const pathObj = new Path(path)
|
|
|
|
pathObj.segs[1].move(-3, 4)
|
|
const seg = path.pathSegList.getItem(1)
|
|
|
|
assert.equal(seg.pathSegTypeAsLetter, 'Q')
|
|
assert.equal(seg.x, 12)
|
|
assert.equal(seg.y, 20)
|
|
assert.equal(seg.x1, 11)
|
|
assert.equal(seg.y1, 12)
|
|
})
|
|
|
|
it('Test svgedit.path.Segment.move for smooth cubic curve', function () {
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M0,0 S13,14 15,16')
|
|
|
|
const [mockPathContext, mockUtilitiesContext] = getMockContexts()
|
|
pathModule.init(mockPathContext)
|
|
utilities.init(mockUtilitiesContext)
|
|
const pathObj = new Path(path)
|
|
|
|
pathObj.segs[1].move(5, -6)
|
|
const seg = path.pathSegList.getItem(1)
|
|
|
|
assert.equal(seg.pathSegTypeAsLetter, 'S')
|
|
assert.equal(seg.x, 20)
|
|
assert.equal(seg.y, 10)
|
|
assert.equal(seg.x2, 18)
|
|
assert.equal(seg.y2, 8)
|
|
})
|
|
|
|
it('Test moving start point moves next quadratic control point', function () {
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M0,0 Q10,0 20,0')
|
|
|
|
const [mockPathContext, mockUtilitiesContext] = getMockContexts()
|
|
pathModule.init(mockPathContext)
|
|
utilities.init(mockUtilitiesContext)
|
|
const pathObj = new Path(path)
|
|
|
|
pathObj.segs[0].move(5, 5)
|
|
const seg = path.pathSegList.getItem(1)
|
|
|
|
assert.equal(seg.pathSegTypeAsLetter, 'Q')
|
|
assert.equal(seg.x, 20)
|
|
assert.equal(seg.y, 0)
|
|
assert.equal(seg.x1, 15)
|
|
assert.equal(seg.y1, 5)
|
|
})
|
|
|
|
it('Test svgedit.path.Segment.moveCtrl', function () {
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M0,0 C11,12 13,14 15,16 Z')
|
|
|
|
const [mockPathContext, mockUtilitiesContext] = getMockContexts()
|
|
pathModule.init(mockPathContext)
|
|
utilities.init(mockUtilitiesContext)
|
|
new Path(path) // eslint-disable-line no-new
|
|
|
|
assert.equal(path.pathSegList.getItem(1).pathSegTypeAsLetter, 'C')
|
|
assert.equal(path.pathSegList.getItem(1).x1, 11)
|
|
assert.equal(path.pathSegList.getItem(1).y1, 12)
|
|
assert.equal(path.pathSegList.getItem(1).x2, 13)
|
|
assert.equal(path.pathSegList.getItem(1).y2, 14)
|
|
assert.equal(path.pathSegList.getItem(1).x, 15)
|
|
assert.equal(path.pathSegList.getItem(1).y, 16)
|
|
|
|
const segment = new Segment(1, path.pathSegList.getItem(1))
|
|
segment.moveCtrl(1, 100, -200)
|
|
assert.equal(path.pathSegList.getItem(1).pathSegTypeAsLetter, 'C')
|
|
assert.equal(path.pathSegList.getItem(1).x1, 111)
|
|
assert.equal(path.pathSegList.getItem(1).y1, -188)
|
|
assert.equal(path.pathSegList.getItem(1).x2, 13)
|
|
assert.equal(path.pathSegList.getItem(1).y2, 14)
|
|
assert.equal(path.pathSegList.getItem(1).x, 15)
|
|
assert.equal(path.pathSegList.getItem(1).y, 16)
|
|
})
|
|
|
|
it('Test svgedit.path.convertPath', function () {
|
|
unitsInit({
|
|
getRoundDigits () { return 5 }
|
|
})
|
|
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M40,55h20v20')
|
|
|
|
const abs = pathModule.convertPath(path)
|
|
assert.equal(abs, 'M40,55L60,55L60,75')
|
|
|
|
const rel = pathModule.convertPath(path, true)
|
|
assert.equal(rel, 'm40,55l20,0l0,20')
|
|
})
|
|
|
|
it('Test convertPath resets after closepath when relative', function () {
|
|
unitsInit({
|
|
getRoundDigits () { return 5 }
|
|
})
|
|
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M10,10 L20,10 Z L15,10')
|
|
const expected = 'm10,10l10,0zl5,0'
|
|
|
|
assert.equal(pathModule.convertPath(path, true), expected)
|
|
assert.equal(convertPathActions(path, true), expected)
|
|
})
|
|
|
|
it('Test recalcRotatedPath preserves zero control points', function () {
|
|
const svg = document.createElementNS(NS.SVG, 'svg')
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M0,0 C0,10 0,20 30,30')
|
|
path.setAttribute('transform', 'rotate(45 0 0)')
|
|
svg.append(path)
|
|
|
|
const [mockPathContext, mockUtilitiesContext] = getMockContexts(svg)
|
|
pathModule.init(mockPathContext)
|
|
utilities.init(mockUtilitiesContext)
|
|
const pathObj = new Path(path)
|
|
pathObj.oldbbox = utilities.getBBox(path)
|
|
|
|
pathModule.recalcRotatedPath()
|
|
|
|
const seg = path.pathSegList.getItem(1)
|
|
assert.equal(seg.pathSegTypeAsLetter, 'C')
|
|
assert.closeTo(seg.x1, 0, 1e-6)
|
|
assert.closeTo(seg.y1, 10, 1e-6)
|
|
assert.closeTo(seg.x2, 0, 1e-6)
|
|
assert.closeTo(seg.y2, 20, 1e-6)
|
|
assert.closeTo(seg.x, 30, 1e-6)
|
|
assert.closeTo(seg.y, 30, 1e-6)
|
|
})
|
|
|
|
it('Test convertPath handles relative arcs', function () {
|
|
unitsInit({
|
|
getRoundDigits () { return 5 }
|
|
})
|
|
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M0,0 a10,20 30 0 1 40,50')
|
|
|
|
const abs = pathModule.convertPath(path)
|
|
assert.ok(abs.includes('A10,20 30 0 1 40,50'))
|
|
|
|
const rel = pathModule.convertPath(path, true)
|
|
assert.ok(rel.includes('a10,20 30 0 1 40,50'))
|
|
})
|
|
|
|
it('Test recalcRotatedPath with no current path', function () {
|
|
const [mockPathContext] = getMockContexts()
|
|
pathModule.init(mockPathContext)
|
|
// path is null initially after init
|
|
pathModule.recalcRotatedPath()
|
|
// Should not throw
|
|
})
|
|
|
|
it('Test recalcRotatedPath with path without rotation', function () {
|
|
const svg = document.createElementNS(NS.SVG, 'svg')
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M0,0 L10,10')
|
|
svg.append(path)
|
|
|
|
const [mockPathContext, mockUtilitiesContext] = getMockContexts(svg)
|
|
pathModule.init(mockPathContext)
|
|
utilities.init(mockUtilitiesContext)
|
|
const pathObj = new Path(path)
|
|
pathObj.oldbbox = utilities.getBBox(path)
|
|
|
|
pathModule.recalcRotatedPath()
|
|
// Should not throw, and path should remain unchanged
|
|
assert.equal(path.getAttribute('d'), 'M0,0 L10,10')
|
|
})
|
|
|
|
it('Test recalcRotatedPath with path without oldbbox', function () {
|
|
const svg = document.createElementNS(NS.SVG, 'svg')
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M0,0 L10,10')
|
|
path.setAttribute('transform', 'rotate(45 0 0)')
|
|
svg.append(path)
|
|
|
|
const [mockPathContext] = getMockContexts(svg)
|
|
pathModule.init(mockPathContext)
|
|
const pathObj = new Path(path)
|
|
pathObj.oldbbox = null
|
|
|
|
pathModule.recalcRotatedPath()
|
|
// Should not throw
|
|
})
|
|
|
|
it('Test Segment class with various pathSegTypes', function () {
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M0,0 H10 V10 Z')
|
|
|
|
const seg1 = new Segment(0, path.pathSegList.getItem(0))
|
|
assert.equal(seg1.index, 0)
|
|
|
|
const seg2 = new Segment(1, path.pathSegList.getItem(1))
|
|
assert.equal(seg2.type, 12) // PATHSEG_LINETO_HORIZONTAL_ABS
|
|
|
|
const seg3 = new Segment(2, path.pathSegList.getItem(2))
|
|
assert.equal(seg3.type, 14) // PATHSEG_LINETO_VERTICAL_ABS
|
|
})
|
|
|
|
it('Test convertPath with smooth cubic curve', function () {
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M0,0 S10,10 20,20')
|
|
|
|
const result = pathModule.convertPath(path, false)
|
|
assert.ok(result.includes('S'))
|
|
})
|
|
|
|
it('Test convertPath with quadratic curve', function () {
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M0,0 Q10,10 20,20')
|
|
|
|
const result = pathModule.convertPath(path, false)
|
|
assert.ok(result.includes('Q'))
|
|
})
|
|
|
|
it('Test Path.update with no pathSegList', function () {
|
|
const svg = document.createElementNS(NS.SVG, 'svg')
|
|
const rect = document.createElementNS(NS.SVG, 'rect')
|
|
svg.append(rect)
|
|
|
|
const [mockPathContext, mockUtilitiesContext] = getMockContexts(svg)
|
|
utilities.init(mockUtilitiesContext)
|
|
pathModule.init(mockPathContext)
|
|
|
|
try {
|
|
const pathObj = new Path(rect)
|
|
pathObj.update()
|
|
} catch (e) {
|
|
// Expected for non-path elements
|
|
assert.ok(true)
|
|
}
|
|
})
|
|
|
|
it('Test convertPath with smooth quadratic curve', function () {
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M0,0 Q10,10 20,0 T40,0')
|
|
|
|
const result = pathModule.convertPath(path, false)
|
|
assert.ok(result.includes('T'))
|
|
})
|
|
|
|
it('Test convertPath with mixed absolute and relative commands', function () {
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M0,0 L10,10 l5,5 L20,20')
|
|
|
|
const abs = pathModule.convertPath(path, false)
|
|
assert.ok(abs.includes('L'))
|
|
|
|
const rel = pathModule.convertPath(path, true)
|
|
assert.ok(rel.includes('l'))
|
|
})
|
|
|
|
it('Test convertPath with horizontal and vertical lines', function () {
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M0,0 H10 V10 h5 v5')
|
|
|
|
const result = pathModule.convertPath(path)
|
|
assert.ok(result.length > 0)
|
|
})
|
|
|
|
it('Test Segment with arc command', function () {
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M0,0 A10,10 0 0 1 20,20')
|
|
|
|
const seg = new Segment(1, path.pathSegList.getItem(1))
|
|
assert.equal(seg.type, 10) // PATHSEG_ARC_ABS
|
|
})
|
|
|
|
it('Test convertPath with quadratic bezier', function () {
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M0,0 Q10,10 20,0')
|
|
|
|
const result = pathModule.convertPath(path)
|
|
assert.ok(result.length > 0)
|
|
})
|
|
|
|
it('Test convertPath with smooth quadratic', function () {
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M0,0 Q10,10 20,0 T30,0')
|
|
|
|
const result = pathModule.convertPath(path)
|
|
assert.ok(result.length > 0)
|
|
})
|
|
|
|
it('Test convertPath with arc sweep flags', function () {
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M0,0 A10,10 0 1 0 20,20')
|
|
|
|
const result = pathModule.convertPath(path)
|
|
assert.ok(result.length > 0)
|
|
})
|
|
|
|
it('Test convertPath with relative arc', function () {
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M10,10 a5,5 0 0 1 10,10')
|
|
|
|
const result = pathModule.convertPath(path)
|
|
assert.ok(result.length > 0)
|
|
})
|
|
|
|
it('Test convertPath with close path', function () {
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M0,0 L10,0 L10,10 Z')
|
|
|
|
const result = pathModule.convertPath(path)
|
|
assert.ok(result.includes('Z') || result.includes('z'))
|
|
})
|
|
|
|
it('Test convertPath with mixed case commands', function () {
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M0,0 L10,10 l5,5 C20,20 25,25 30,20')
|
|
|
|
const result = pathModule.convertPath(path)
|
|
assert.ok(result.length > 0)
|
|
})
|
|
|
|
it('Test Segment getItem', function () {
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M0,0 L10,10 L20,20')
|
|
|
|
const seg = new Segment(1, path.pathSegList.getItem(1))
|
|
assert.ok(seg.type)
|
|
})
|
|
|
|
it('Test convertPath with relative smooth cubic', function () {
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M0,0 C10,10 20,10 30,0 s10,10 20,0')
|
|
|
|
const result = pathModule.convertPath(path)
|
|
assert.ok(result.length > 0)
|
|
})
|
|
|
|
it('Test convertPath with negative coordinates', function () {
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M-10,-10 L-20,-20 L-30,-15')
|
|
|
|
const result = pathModule.convertPath(path)
|
|
assert.ok(result.length > 0)
|
|
})
|
|
|
|
it('Test convertPath with decimal coordinates', function () {
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M0.5,0.5 L10.25,10.75')
|
|
|
|
const result = pathModule.convertPath(path)
|
|
assert.ok(result.length > 0)
|
|
})
|
|
|
|
it('Test Segment with move command', function () {
|
|
const path = document.createElementNS(NS.SVG, 'path')
|
|
path.setAttribute('d', 'M10,10 L20,20')
|
|
|
|
const seg = new Segment(0, path.pathSegList.getItem(0))
|
|
assert.equal(seg.type, 2) // PATHSEG_MOVETO_ABS
|
|
})
|
|
})
|