
Note that the tests for Lumina are failing, but that's not related to the chai upgrade, rather it seems these tests fail because of issues in the design that we'll tackle later (it's a brand new design yet to be released).
1063 lines
37 KiB
JavaScript
1063 lines
37 KiB
JavaScript
import { expect } from 'chai'
|
|
import { round, Path, Point } from '../src/index.mjs'
|
|
import { pathsProxy } from '../src/path.mjs'
|
|
|
|
describe('Path', () => {
|
|
describe('smurve', () => {
|
|
it('Should draw a smurve', () => {
|
|
const points = {}
|
|
points.from = new Point(10, 20)
|
|
points.cp1 = new Point(40, 10)
|
|
points.cp2 = new Point(60, 30)
|
|
points.to = new Point(90, 20)
|
|
points.scp2 = new Point(140, 10)
|
|
points.sto = new Point(170, 20)
|
|
|
|
const test = new Path()
|
|
.move(points.from)
|
|
.curve(points.cp1, points.cp2, points.to)
|
|
.smurve(points.scp2, points.sto)
|
|
|
|
expect(round(test.ops[2].cp1.x)).to.equal(120)
|
|
expect(round(test.ops[2].cp1.y)).to.equal(10)
|
|
})
|
|
|
|
it('Should draw a smurve_', () => {
|
|
const points = {}
|
|
points.from = new Point(10, 20)
|
|
points.cp1 = new Point(40, 10)
|
|
points.cp2 = new Point(60, 30)
|
|
points.to = new Point(90, 20)
|
|
points.sto = new Point(170, 20)
|
|
|
|
const test = new Path()
|
|
.move(points.from)
|
|
.curve(points.cp1, points.cp2, points.to)
|
|
.smurve_(points.sto)
|
|
|
|
expect(round(test.ops[2].cp1.x)).to.equal(120)
|
|
expect(round(test.ops[2].cp1.y)).to.equal(10)
|
|
})
|
|
|
|
it('Should log a warning when passing a non-Point to smurve()', () => {
|
|
const points = {}
|
|
points.from = new Point(10, 20)
|
|
points.cp1 = new Point(40, 10)
|
|
points.cp2 = new Point(60, 30)
|
|
points.to = new Point(90, 20)
|
|
|
|
const messages = []
|
|
const log = { warn: (msg) => messages.push(msg) }
|
|
new Path()
|
|
.__withLog(log)
|
|
.move(points.from)
|
|
.curve(points.cp1, points.cp2, points.to)
|
|
.smurve('hi', 'there')
|
|
|
|
expect(messages.length).to.equal(2)
|
|
expect(messages[0]).to.equal('Called `Path.smurve(cp2, to)` but `to` is not a `Point` object')
|
|
})
|
|
|
|
it('Should log a warning when passing a non-Point to smurve_()', () => {
|
|
const messages = []
|
|
const log = { warn: (msg) => messages.push(msg) }
|
|
try {
|
|
new Path().__withLog(log).smurve_('hi')
|
|
} catch (e) {
|
|
expect('' + e).to.contain("TypeError: Cannot read properties of undefined (reading 'cp2')")
|
|
} finally {
|
|
expect(messages.length).to.equal(1)
|
|
expect(messages[0]).to.equal('Called `Path.smurve_(to)` but `to` is not a `Point` object')
|
|
}
|
|
})
|
|
})
|
|
|
|
it('Should log a warning when passing a non-Path to the paths proxy', () => {
|
|
const messages = []
|
|
const log = { warn: (msg) => messages.push(msg) }
|
|
const pathsObj = {}
|
|
const paths = pathsProxy(pathsObj, log)
|
|
paths.set(pathsObj, 'test', 'Writing code can get very lonely sometimes')
|
|
|
|
expect(messages.length).to.equal(2)
|
|
expect(messages[0]).to.equal('`paths.test` was set with a value that is not a `Path` object')
|
|
expect(messages[1]).to.equal('Could not set `name` property on `paths.test`')
|
|
})
|
|
|
|
describe('offset', () => {
|
|
it('Should offset a line', () => {
|
|
const line = new Path().move(new Point(0, 0)).line(new Point(0, 40))
|
|
const offLine = line.offset(10)
|
|
const bbox = offLine.bbox()
|
|
expect(bbox.bottomRight.x).to.equal(-10)
|
|
expect(bbox.bottomRight.y).to.equal(40)
|
|
})
|
|
|
|
it('Should offset a curve', () => {
|
|
const curve = new Path()
|
|
.move(new Point(0, 0))
|
|
.curve(new Point(0, 40), new Point(123, 34), new Point(23, 4))
|
|
const offset = curve.offset(10)
|
|
const bbox = offset.bbox()
|
|
expect(round(bbox.bottomRight.x)).to.equal(72.18)
|
|
expect(round(bbox.bottomRight.y)).to.equal(38.26)
|
|
})
|
|
|
|
it('Should offset a curve where cp1 = start', () => {
|
|
const curve = new Path().move(new Point(0, 0))._curve(new Point(123, 34), new Point(23, 4))
|
|
const offset = curve.offset(10)
|
|
const bbox = offset.bbox()
|
|
expect(round(bbox.bottomRight.x)).to.equal(72.63)
|
|
expect(round(bbox.bottomRight.y)).to.equal(26.47)
|
|
})
|
|
|
|
it('Should offset a curve where cp2 = end', () => {
|
|
const curve = new Path().move(new Point(0, 0)).curve_(new Point(40, 0), new Point(123, 34))
|
|
const offset = curve.offset(10)
|
|
const bbox = offset.bbox()
|
|
expect(round(bbox.bottomRight.x)).to.equal(119.86)
|
|
expect(round(bbox.bottomRight.y)).to.equal(43.49)
|
|
})
|
|
})
|
|
|
|
describe('length', () => {
|
|
it('Should return the length of a line', () => {
|
|
const line = new Path().move(new Point(0, 0)).line(new Point(40, 0))
|
|
expect(line.length()).to.equal(40)
|
|
})
|
|
|
|
it('Should return the length of a curve', () => {
|
|
const curve = new Path()
|
|
.move(new Point(0, 0))
|
|
.curve(new Point(0, 40), new Point(123, 34), new Point(23, 4))
|
|
.close()
|
|
expect(round(curve.length())).to.equal(145.11)
|
|
})
|
|
})
|
|
|
|
it('Should return the rough length of a curve', () => {
|
|
const curve = new Path()
|
|
.move(new Point(0, 0))
|
|
.curve(new Point(0, 50), new Point(100, 50), new Point(100, 0))
|
|
.close()
|
|
expect(round(curve.roughLength())).to.equal(300)
|
|
})
|
|
|
|
it('Should return the rough length of a line', () => {
|
|
const line = new Path().move(new Point(0, 0)).line(new Point(0, 50))
|
|
expect(round(line.roughLength())).to.equal(50)
|
|
})
|
|
|
|
it('Should return the path start point', () => {
|
|
const curve = new Path()
|
|
.move(new Point(123, 456))
|
|
.curve(new Point(0, 40), new Point(123, 34), new Point(23, 4))
|
|
.close()
|
|
expect(curve.start().x).to.equal(123)
|
|
expect(curve.start().y).to.equal(456)
|
|
})
|
|
|
|
it('Should return the path end point', () => {
|
|
const curve = new Path()
|
|
.move(new Point(123, 456))
|
|
.curve(new Point(0, 40), new Point(123, 34), new Point(23, 4))
|
|
.close()
|
|
expect(curve.end().x).to.equal(123)
|
|
expect(curve.end().y).to.equal(456)
|
|
})
|
|
|
|
it('Should calculate that path boundary', () => {
|
|
const curve = new Path()
|
|
.move(new Point(123, 456))
|
|
.curve(new Point(0, 40), new Point(123, 34), new Point(230, 4))
|
|
curve.__boundary()
|
|
expect(curve.topLeft.x).to.equal(71.6413460920667)
|
|
expect(curve.topLeft.y).to.equal(4)
|
|
expect(curve.bottomRight.x).to.equal(230)
|
|
expect(curve.bottomRight.y).to.equal(456)
|
|
})
|
|
|
|
it('Should clone a path', () => {
|
|
const curve = new Path()
|
|
.move(new Point(123, 456))
|
|
.curve(new Point(0, 40), new Point(123, 34), new Point(230, 4))
|
|
let b = curve.clone()
|
|
b.__boundary()
|
|
expect(b.topLeft.x).to.equal(71.6413460920667)
|
|
expect(b.topLeft.y).to.equal(4)
|
|
b = b.clone()
|
|
expect(b.bottomRight.x).to.equal(230)
|
|
expect(b.bottomRight.y).to.equal(456)
|
|
})
|
|
|
|
it('Should join paths', () => {
|
|
const line = new Path().move(new Point(0, 0)).line(new Point(0, 40))
|
|
const curve = new Path()
|
|
.move(new Point(123, 456))
|
|
.curve(new Point(0, 40), new Point(123, 34), new Point(230, 4))
|
|
const joint = curve.join(line)
|
|
expect(joint.ops.length).to.equal(4)
|
|
})
|
|
|
|
it('Should join paths that have noop operations', () => {
|
|
const line = new Path().move(new Point(0, 0)).line(new Point(0, 40)).noop('test1')
|
|
const curve = new Path()
|
|
.move(new Point(123, 456))
|
|
.curve(new Point(0, 40), new Point(123, 34), new Point(230, 4))
|
|
.noop('test2')
|
|
const joint = curve.join(line)
|
|
expect(joint.ops.length).to.equal(6)
|
|
})
|
|
|
|
it('Should throw error when joining a closed paths', () => {
|
|
const line = new Path().move(new Point(0, 0)).line(new Point(0, 40))
|
|
const curve = new Path()
|
|
.move(new Point(123, 456))
|
|
.curve(new Point(0, 40), new Point(123, 34), new Point(230, 4))
|
|
.close()
|
|
expect(() => curve.join(line)).to.throw()
|
|
})
|
|
|
|
it('Should shift along a line', () => {
|
|
const line = new Path().move(new Point(0, 0)).line(new Point(0, 40))
|
|
expect(line.shiftAlong(20).y).to.equal(20)
|
|
})
|
|
|
|
it('Should not shift along a path/line if we end up on the end point', () => {
|
|
const line = new Path().move(new Point(0, 0)).line(new Point(10, 0))
|
|
expect(line.shiftAlong(10).x).to.equal(10)
|
|
})
|
|
|
|
it('Should shift along lines', () => {
|
|
const line = new Path().move(new Point(0, 0)).line(new Point(0, 40)).line(new Point(100, 40))
|
|
expect(line.shiftAlong(50).x).to.equal(10)
|
|
expect(line.shiftAlong(50).y).to.equal(40)
|
|
})
|
|
|
|
it('Should shift along curve + line', () => {
|
|
const test = new Path()
|
|
.move(new Point(0, 0))
|
|
.line(new Point(0, 40))
|
|
.curve(new Point(40, 40), new Point(40, 0), new Point(200, 0))
|
|
.line(new Point(200, 400))
|
|
expect(round(test.shiftAlong(500).x)).to.equal(200)
|
|
expect(round(test.shiftAlong(500).y)).to.equal(253.74)
|
|
})
|
|
|
|
it("Should throw an error when shifting along path further than it's long", () => {
|
|
const test = new Path().move(new Point(0, 0)).line(new Point(0, 40)).line(new Point(200, 400))
|
|
expect(() => test.shiftAlong(500)).to.throw()
|
|
})
|
|
|
|
it('Should shift along with sufficient precision', () => {
|
|
const test = new Path()
|
|
.move(new Point(0, 0))
|
|
.curve(new Point(123, 123), new Point(-123, 456), new Point(456, -123))
|
|
const a = test.shiftAlong(100)
|
|
const b = test.reverse().shiftAlong(test.length() - 100)
|
|
expect(a.dist(b)).to.below(0.2)
|
|
})
|
|
|
|
it('Should shift fraction with sufficient precision', () => {
|
|
const test = new Path()
|
|
.move(new Point(0, 0))
|
|
.curve(new Point(123, 123), new Point(-123, 456), new Point(456, -123))
|
|
const a = test.shiftFractionAlong(0.5)
|
|
const b = test.reverse().shiftFractionAlong(0.5)
|
|
expect(a.dist(b)).to.below(0.2)
|
|
})
|
|
|
|
it('Should shift a fraction along a line', () => {
|
|
const line = new Path().move(new Point(0, 0)).line(new Point(0, 40)).line(new Point(100, 40))
|
|
expect(round(line.shiftFractionAlong(0.5).x)).to.equal(30)
|
|
expect(round(line.shiftFractionAlong(0.5).y)).to.equal(40)
|
|
})
|
|
|
|
it('Should find the bounding box of a line', () => {
|
|
let line = new Path().move(new Point(3, 2)).line(new Point(10, 40))
|
|
let box = line.bbox()
|
|
expect(box.topLeft.x).to.equal(3)
|
|
expect(box.topLeft.y).to.equal(2)
|
|
expect(box.bottomRight.x).to.equal(10)
|
|
expect(box.bottomRight.y).to.equal(40)
|
|
|
|
line = new Path().move(new Point(10, 40)).line(new Point(3, 2))
|
|
box = line.bbox()
|
|
expect(box.topLeft.x).to.equal(3)
|
|
expect(box.topLeft.y).to.equal(2)
|
|
expect(box.bottomRight.x).to.equal(10)
|
|
expect(box.bottomRight.y).to.equal(40)
|
|
|
|
line = new Path().move(new Point(1, 40)).line(new Point(31, 2))
|
|
box = line.bbox()
|
|
expect(box.topLeft.x).to.equal(1)
|
|
expect(box.topLeft.y).to.equal(2)
|
|
expect(box.bottomRight.x).to.equal(31)
|
|
expect(box.bottomRight.y).to.equal(40)
|
|
|
|
line = new Path().move(new Point(31, 2)).line(new Point(1, 40))
|
|
box = line.bbox()
|
|
expect(box.topLeft.x).to.equal(1)
|
|
expect(box.topLeft.y).to.equal(2)
|
|
expect(box.bottomRight.x).to.equal(31)
|
|
expect(box.bottomRight.y).to.equal(40)
|
|
|
|
line = new Path().move(new Point(11, 2)).line(new Point(11, 40))
|
|
box = line.bbox()
|
|
expect(box.topLeft.x).to.equal(11)
|
|
expect(box.topLeft.y).to.equal(2)
|
|
expect(box.bottomRight.x).to.equal(11)
|
|
expect(box.bottomRight.y).to.equal(40)
|
|
|
|
line = new Path().move(new Point(11, 40)).line(new Point(11, 2))
|
|
box = line.bbox()
|
|
expect(box.topLeft.x).to.equal(11)
|
|
expect(box.topLeft.y).to.equal(2)
|
|
expect(box.bottomRight.x).to.equal(11)
|
|
expect(box.bottomRight.y).to.equal(40)
|
|
|
|
line = new Path().move(new Point(11, 12)).line(new Point(41, 12))
|
|
box = line.bbox()
|
|
expect(box.topLeft.x).to.equal(11)
|
|
expect(box.topLeft.y).to.equal(12)
|
|
expect(box.bottomRight.x).to.equal(41)
|
|
expect(box.bottomRight.y).to.equal(12)
|
|
|
|
line = new Path().move(new Point(41, 12)).line(new Point(11, 12))
|
|
box = line.bbox()
|
|
expect(box.topLeft.x).to.equal(11)
|
|
expect(box.topLeft.y).to.equal(12)
|
|
expect(box.bottomRight.x).to.equal(41)
|
|
expect(box.bottomRight.y).to.equal(12)
|
|
})
|
|
|
|
it('Should find the bounding box of a curve', () => {
|
|
const curve = new Path()
|
|
.move(new Point(123, 456))
|
|
.curve(new Point(0, 40), new Point(123, 34), new Point(230, 4))
|
|
.close()
|
|
const box = curve.bbox()
|
|
expect(round(box.topLeft.x)).to.equal(71.64)
|
|
expect(box.topLeft.y).to.equal(4)
|
|
expect(box.bottomRight.x).to.equal(230)
|
|
expect(box.bottomRight.y).to.equal(456)
|
|
})
|
|
|
|
it('Should reverse a path', () => {
|
|
const test = new Path()
|
|
.move(new Point(123, 456))
|
|
.line(new Point(12, 23))
|
|
.curve(new Point(0, 40), new Point(123, 34), new Point(230, 4))
|
|
.close()
|
|
let rev = test.reverse()
|
|
let tb = test.bbox()
|
|
let rb = rev.bbox()
|
|
expect(tb.topLeft.x).to.equal(rb.topLeft.x)
|
|
expect(tb.topLeft.y).to.equal(rb.topLeft.y)
|
|
expect(tb.bottomRight.x).to.equal(rb.bottomRight.x)
|
|
expect(tb.bottomRight.y).to.equal(rb.bottomRight.y)
|
|
expect(rev.ops[1].type).to.equal('curve')
|
|
expect(rev.ops[2].type).to.equal('line')
|
|
})
|
|
|
|
it('Should find the edges of a path', () => {
|
|
const test = new Path()
|
|
.move(new Point(45, 60))
|
|
.line(new Point(10, 30))
|
|
.curve(new Point(40, 20), new Point(50, -30), new Point(90, 30))
|
|
.curve(new Point(90, 190), new Point(-60, 90), new Point(45, 60))
|
|
.close()
|
|
expect(round(test.edge('topLeft').x)).to.equal(7.7)
|
|
expect(round(test.edge('topLeft').y)).to.equal(0.97)
|
|
expect(round(test.edge('bottomLeft').x)).to.equal(7.7)
|
|
expect(round(test.edge('bottomLeft').y)).to.equal(118.46)
|
|
expect(round(test.edge('bottomRight').x)).to.equal(90)
|
|
expect(round(test.edge('bottomRight').y)).to.equal(118.46)
|
|
expect(round(test.edge('topRight').x)).to.equal(90)
|
|
expect(round(test.edge('topRight').y)).to.equal(0.97)
|
|
expect(round(test.edge('left').x)).to.equal(7.7)
|
|
expect(round(test.edge('left').y)).to.equal(91.8)
|
|
expect(round(test.edge('bottom').x)).to.equal(40.63)
|
|
expect(round(test.edge('bottom').y)).to.equal(118.46)
|
|
expect(round(test.edge('right').x)).to.equal(89.76)
|
|
expect(round(test.edge('right').y)).to.equal(29.64)
|
|
expect(round(test.edge('top').x)).to.equal(55.98)
|
|
expect(round(test.edge('top').y)).to.equal(0.97)
|
|
})
|
|
|
|
it('Should find the edges of a path for corner cases', () => {
|
|
let test = new Path().move(new Point(-45, -60)).line(new Point(45, 60))
|
|
expect(round(test.edge('top').x)).to.equal(-45)
|
|
expect(round(test.edge('top').y)).to.equal(-60)
|
|
expect(round(test.edge('left').x)).to.equal(-45)
|
|
expect(round(test.edge('left').y)).to.equal(-60)
|
|
expect(round(test.edge('bottom').x)).to.equal(45)
|
|
expect(round(test.edge('bottom').y)).to.equal(60)
|
|
expect(round(test.edge('right').x)).to.equal(45)
|
|
expect(round(test.edge('right').y)).to.equal(60)
|
|
test = new Path().move(new Point(45, 60)).line(new Point(-45, -60))
|
|
expect(round(test.edge('top').x)).to.equal(-45)
|
|
expect(round(test.edge('top').y)).to.equal(-60)
|
|
expect(round(test.edge('left').x)).to.equal(-45)
|
|
expect(round(test.edge('left').y)).to.equal(-60)
|
|
expect(round(test.edge('bottom').x)).to.equal(45)
|
|
expect(round(test.edge('bottom').y)).to.equal(60)
|
|
expect(round(test.edge('right').x)).to.equal(45)
|
|
expect(round(test.edge('right').y)).to.equal(60)
|
|
})
|
|
|
|
it('Should find the edge of a path for this edge-case', () => {
|
|
const test = new Path()
|
|
.move(new Point(-109.7, 77))
|
|
.curve(new Point(-27.33, 99.19), new Point(-39.45, 137.4), new Point(-61.52, 219.77))
|
|
expect(round(test.edge('right').x)).to.equal(-45.22)
|
|
expect(round(test.edge('right').y)).to.equal(139.4)
|
|
})
|
|
|
|
it('Should find where a path intersects with an X value', () => {
|
|
const A = new Point(95, 50)
|
|
const B = new Point(10, 30)
|
|
const BCp2 = new Point(40, 20)
|
|
const C = new Point(90, 30)
|
|
const CCp1 = new Point(50, -30)
|
|
const D = new Point(50, 130)
|
|
const DCp1 = new Point(150, 30)
|
|
const test = new Path().move(A).line(B).curve(BCp2, CCp1, C).curve(DCp1, DCp1, D).close()
|
|
const intersections = test.intersectsX(60)
|
|
expect(intersections.length).to.equal(4)
|
|
expect(round(intersections[0].x)).to.equal(60)
|
|
expect(round(intersections[0].y)).to.equal(41.76)
|
|
expect(round(intersections[1].x)).to.equal(60)
|
|
expect(round(intersections[1].y)).to.equal(1.45)
|
|
expect(round(intersections[2].x)).to.equal(60)
|
|
expect(round(intersections[2].y)).to.equal(120)
|
|
expect(round(intersections[3].x)).to.equal(60)
|
|
expect(round(intersections[3].y)).to.equal(112.22)
|
|
})
|
|
|
|
it('Should find where a path intersects with an Y value', () => {
|
|
const A = new Point(95, 50)
|
|
const B = new Point(10, 30)
|
|
const BCp2 = new Point(40, 20)
|
|
const C = new Point(90, 30)
|
|
const CCp1 = new Point(50, -30)
|
|
const D = new Point(50, 130)
|
|
const DCp1 = new Point(150, 30)
|
|
const test = new Path().move(A).line(B).curve(BCp2, CCp1, C).curve(DCp1, DCp1, D).close()
|
|
let intersections = test.intersectsY(60)
|
|
expect(intersections.length).to.equal(2)
|
|
expect(round(intersections[0].x)).to.equal(117.83)
|
|
expect(round(intersections[0].y)).to.equal(60)
|
|
expect(round(intersections[1].x)).to.equal(89.38)
|
|
expect(round(intersections[1].y)).to.equal(60)
|
|
})
|
|
|
|
it('Should throw an error when not passing a value to path.intersectsX', () => {
|
|
const test = new Path()
|
|
expect(() => test.intersectsX()).to.throw()
|
|
expect(() => test.intersectsY()).to.throw()
|
|
})
|
|
|
|
it('Should find the intersections between two paths', () => {
|
|
const A = new Point(45, 60)
|
|
const B = new Point(10, 30)
|
|
const BCp2 = new Point(40, 20)
|
|
const C = new Point(90, 30)
|
|
const CCp1 = new Point(50, -30)
|
|
const D = new Point(50, 130)
|
|
const DCp1 = new Point(150, 30)
|
|
|
|
const _A = new Point(55, 40)
|
|
const _B = new Point(0, 55)
|
|
const _BCp2 = new Point(40, -20)
|
|
const _C = new Point(90, 40)
|
|
const _CCp1 = new Point(50, -30)
|
|
const _D = new Point(40, 120)
|
|
const _DCp1 = new Point(180, 40)
|
|
|
|
const example1 = new Path().move(A).line(B).curve(BCp2, CCp1, C).curve(DCp1, DCp1, D)
|
|
const example2 = new Path().move(_A).line(_B).curve(_BCp2, _CCp1, _C).curve(_DCp1, _DCp1, _D)
|
|
let intersections = example1.intersects(example2)
|
|
expect(intersections.length).to.equal(6)
|
|
expect(round(intersections[0].x)).to.equal(29.71)
|
|
expect(round(intersections[0].y)).to.equal(46.9)
|
|
expect(round(intersections[1].x)).to.equal(12.48)
|
|
expect(round(intersections[1].y)).to.equal(32.12)
|
|
expect(round(intersections[2].x)).to.equal(14.84)
|
|
expect(round(intersections[2].y)).to.equal(27.98)
|
|
expect(round(intersections[3].x)).to.equal(66.33)
|
|
expect(round(intersections[3].y)).to.equal(4.1)
|
|
expect(round(intersections[4].x)).to.equal(130.65)
|
|
expect(round(intersections[4].y)).to.equal(40.52)
|
|
expect(round(intersections[5].x)).to.equal(86.52)
|
|
expect(round(intersections[5].y)).to.equal(93.31)
|
|
})
|
|
|
|
it('Should throw an error when running path.intersect on an identical path', () => {
|
|
const test = new Path()
|
|
expect(() => test.intersects(test)).to.throw()
|
|
})
|
|
|
|
it('Should divide a path', () => {
|
|
const A = new Point(45, 60)
|
|
const B = new Point(10, 30)
|
|
const BCp2 = new Point(40, 20)
|
|
const C = new Point(90, 30)
|
|
const CCp1 = new Point(50, -30)
|
|
const D = new Point(-60, 90)
|
|
const E = new Point(90, 190)
|
|
const test = new Path().move(A).line(B).curve(BCp2, CCp1, C).curve(E, D, A).close()
|
|
let divided = test.divide()
|
|
expect(divided.length).to.equal(4)
|
|
expect(divided[0].ops[0].type).to.equal('move')
|
|
expect(divided[0].ops[0].to.x).to.equal(45)
|
|
expect(divided[0].ops[0].to.y).to.equal(60)
|
|
expect(divided[0].ops[1].type).to.equal('line')
|
|
expect(divided[0].ops[1].to.x).to.equal(10)
|
|
expect(divided[0].ops[1].to.y).to.equal(30)
|
|
expect(divided[1].ops[0].type).to.equal('move')
|
|
expect(divided[1].ops[0].to.x).to.equal(10)
|
|
expect(divided[1].ops[0].to.y).to.equal(30)
|
|
expect(divided[1].ops[1].type).to.equal('curve')
|
|
expect(divided[1].ops[1].cp1.x).to.equal(40)
|
|
expect(divided[1].ops[1].cp1.y).to.equal(20)
|
|
expect(divided[1].ops[1].cp2.x).to.equal(50)
|
|
expect(divided[1].ops[1].cp2.y).to.equal(-30)
|
|
expect(divided[1].ops[1].to.x).to.equal(90)
|
|
expect(divided[1].ops[1].to.y).to.equal(30)
|
|
expect(divided[2].ops[0].type).to.equal('move')
|
|
expect(divided[2].ops[0].to.x).to.equal(90)
|
|
expect(divided[2].ops[0].to.y).to.equal(30)
|
|
expect(divided[2].ops[1].type).to.equal('curve')
|
|
expect(divided[2].ops[1].cp1.x).to.equal(90)
|
|
expect(divided[2].ops[1].cp1.y).to.equal(190)
|
|
expect(divided[2].ops[1].cp2.x).to.equal(-60)
|
|
expect(divided[2].ops[1].cp2.y).to.equal(90)
|
|
expect(divided[2].ops[1].to.x).to.equal(45)
|
|
expect(divided[2].ops[1].to.y).to.equal(60)
|
|
expect(divided[3].ops[0].type).to.equal('move')
|
|
expect(divided[3].ops[0].to.x).to.equal(45)
|
|
expect(divided[3].ops[0].to.y).to.equal(60)
|
|
expect(divided[3].ops[1].type).to.equal('line')
|
|
expect(divided[3].ops[1].to.x).to.equal(45)
|
|
expect(divided[3].ops[1].to.y).to.equal(60)
|
|
})
|
|
|
|
it('Should split a path on a curve', () => {
|
|
const A = new Point(45, 60)
|
|
const B = new Point(10, 30)
|
|
const BCp2 = new Point(40, 20)
|
|
const C = new Point(90, 30)
|
|
const CCp1 = new Point(50, -30)
|
|
const D = new Point(50, 130)
|
|
const DCp1 = new Point(150, 30)
|
|
|
|
const test = new Path().move(A).line(B).curve(BCp2, CCp1, C).curve(DCp1, DCp1, D)
|
|
|
|
const split = test.shiftAlong(120)
|
|
let halves = test.split(split)
|
|
let curve = halves[0].ops.pop()
|
|
expect(curve.type).to.equal('curve')
|
|
expect(round(curve.cp1.x)).to.equal(35.08)
|
|
expect(round(curve.cp1.y)).to.equal(21.64)
|
|
expect(round(curve.cp2.x)).to.equal(46.18)
|
|
expect(round(curve.cp2.y)).to.equal(-14.67)
|
|
expect(round(curve.to.x)).to.equal(72.51)
|
|
expect(round(curve.to.y)).to.equal(8.69)
|
|
})
|
|
|
|
it('Should split a path on a line', () => {
|
|
const A = new Point(45, 60)
|
|
const B = new Point(10, 30)
|
|
const BCp2 = new Point(40, 20)
|
|
const C = new Point(90, 30)
|
|
const CCp1 = new Point(50, -30)
|
|
const D = new Point(50, 130)
|
|
const DCp1 = new Point(150, 30)
|
|
|
|
const test = new Path().move(A).line(B).curve(BCp2, CCp1, C).curve(DCp1, DCp1, D)
|
|
|
|
const split = test.shiftAlong(20)
|
|
let halves = test.split(split)
|
|
let line = halves[0].ops.pop()
|
|
expect(line.type).to.equal('line')
|
|
expect(round(line.to.x)).to.equal(29.81)
|
|
expect(round(line.to.y)).to.equal(46.98)
|
|
})
|
|
|
|
it('Should split a path on a line joint', () => {
|
|
const a = new Point(45, 60)
|
|
const b = new Point(10, 30)
|
|
const c = new Point(90, 30)
|
|
const test = new Path().move(a).line(b).line(c)
|
|
|
|
let halves = test.split(b)
|
|
expect(halves[0].ops[1].to.x).to.equal(10)
|
|
expect(halves[0].ops[1].to.y).to.equal(30)
|
|
expect(halves[1].ops[0].to.x).to.equal(10)
|
|
expect(halves[1].ops[0].to.y).to.equal(30)
|
|
})
|
|
|
|
it('Should split a path on roughly a line joint', () => {
|
|
const a = new Point(45, 60)
|
|
const b = new Point(10, 30)
|
|
const c = new Point(90, 30)
|
|
const test = new Path().move(a).line(b).line(c)
|
|
|
|
let halves = test.split(new Point(10.1, 29.9))
|
|
expect(halves[0].ops[1].to.x).to.equal(10.1)
|
|
expect(halves[0].ops[1].to.y).to.equal(29.9)
|
|
expect(halves[1].ops[0].to.x).to.equal(10.1)
|
|
expect(halves[1].ops[0].to.y).to.equal(29.9)
|
|
})
|
|
|
|
it('Should split a path on a curve joint', () => {
|
|
const a = new Point(45, 60)
|
|
const b = new Point(10, 30)
|
|
const c = new Point(90, 30)
|
|
const test = new Path().move(a)._curve(b, b)._curve(c, c)
|
|
|
|
let halves = test.split(b)
|
|
expect(halves[0].ops[1].to.x).to.equal(10)
|
|
expect(halves[0].ops[1].to.y).to.equal(30)
|
|
expect(halves[1].ops[0].to.x).to.equal(10)
|
|
expect(halves[1].ops[0].to.y).to.equal(30)
|
|
})
|
|
|
|
it('Should trim a path when lines overlap', () => {
|
|
const A = new Point(0, 0)
|
|
const B = new Point(100, 100)
|
|
const C = new Point(0, 100)
|
|
const D = new Point(100, 0)
|
|
|
|
let test = new Path().move(new Point(0, 20)).line(A).line(B).line(C).line(D).line(A).trim()
|
|
|
|
expect(test.ops.length).to.equal(5)
|
|
expect(test.ops[2].to.x).to.equal(50)
|
|
expect(test.ops[2].to.y).to.equal(50)
|
|
})
|
|
|
|
it('Should trim a path when a line overlaps with a curve', () => {
|
|
const A = new Point(0, 0)
|
|
const B = new Point(100, 100)
|
|
const C = new Point(0, 100)
|
|
const D = new Point(100, 0)
|
|
|
|
let test = new Path()
|
|
.move(new Point(0, 20))
|
|
.line(A)
|
|
.curve(D, B, B)
|
|
.line(C)
|
|
.line(D)
|
|
.line(A)
|
|
.trim()
|
|
|
|
expect(test.ops.length).to.equal(5)
|
|
expect(round(test.ops[2].to.x)).to.equal(72.19)
|
|
expect(round(test.ops[2].to.y)).to.equal(27.81)
|
|
})
|
|
|
|
it('Should trim a path when a curves overlap', () => {
|
|
const A = new Point(0, 0)
|
|
const B = new Point(100, 100)
|
|
const C = new Point(0, 100)
|
|
const D = new Point(100, 0)
|
|
|
|
let test = new Path()
|
|
.move(new Point(0, 20))
|
|
.line(A)
|
|
.curve(D, B, B)
|
|
.line(C)
|
|
.curve(C, A, D)
|
|
.line(A)
|
|
.trim()
|
|
|
|
expect(test.ops.length).to.equal(5)
|
|
expect(round(test.ops[2].to.x)).to.equal(50)
|
|
expect(round(test.ops[2].to.y)).to.equal(11.01)
|
|
})
|
|
|
|
it('Should translate a path', () => {
|
|
const A = new Point(0, 0)
|
|
const B = new Point(100, 100)
|
|
const C = new Point(0, 100)
|
|
const D = new Point(100, 0)
|
|
|
|
let base = new Path().move(A).curve(B, C, D)
|
|
let test = base.translate(10, 20)
|
|
|
|
expect(test.ops.length).to.equal(2)
|
|
expect(test.ops[0].to.x).to.equal(10)
|
|
expect(test.ops[0].to.y).to.equal(20)
|
|
expect(test.ops[1].to.x).to.equal(110)
|
|
expect(test.ops[1].to.y).to.equal(20)
|
|
})
|
|
|
|
it('Calling translate with non-numbers should generate a warning', () => {
|
|
const log = []
|
|
const p = new Path()
|
|
p.log = { warn: (msg) => log.push(msg) }
|
|
p.translate('a', 'b')
|
|
expect(log.length).to.equal(2)
|
|
expect(log[0]).to.equal('Called `Path.translate(x, y)` but `x` is not a number')
|
|
expect(log[1]).to.equal('Called `Path.translate(x, y)` but `y` is not a number')
|
|
})
|
|
|
|
it('Should add a path attribute', () => {
|
|
const line = new Path()
|
|
.move(new Point(0, 0))
|
|
.line(new Point(0, 40))
|
|
.attr('class', 'foo')
|
|
.attr('class', 'bar')
|
|
expect(line.attributes.get('class')).to.equal('foo bar')
|
|
})
|
|
|
|
it('Should overwrite a path attribute', () => {
|
|
const line = new Path()
|
|
line.log = { debug: () => {} }
|
|
line
|
|
.move(new Point(0, 0))
|
|
.line(new Point(0, 40))
|
|
.attr('class', 'foo')
|
|
.attr('class', 'bar')
|
|
.attr('class', 'overwritten', true)
|
|
|
|
// Paths from shorthand have the log method
|
|
expect(line.attributes.get('class')).to.equal('overwritten')
|
|
})
|
|
|
|
it('Should move along a path even if it lands just on a joint', () => {
|
|
const curve = new Path()
|
|
.move(new Point(20.979322245694167, -219.8547313525503))
|
|
._curve(
|
|
new Point(35.33122482627704, -153.54225517257478),
|
|
new Point(61.99376179214562, -105.99242252587702)
|
|
)
|
|
.curve(
|
|
new Point(88.85254026593002, -58.092613773317105),
|
|
new Point(136.13264764576948, -11.692646171119936),
|
|
new Point(170.69593749999996, -4.180844669736632e-14)
|
|
)
|
|
const test = curve.shiftAlong(121.36690836797631)
|
|
expect(test).to.be.instanceOf(Point)
|
|
})
|
|
|
|
it('Should add log methods to a path', () => {
|
|
const log = () => 'hello'
|
|
const p1 = new Path(10, 20).__withLog(log)
|
|
expect(p1.log()).to.equal('hello')
|
|
})
|
|
|
|
it('Should add log methods to a path', () => {
|
|
const log = () => 'hello'
|
|
const p1 = new Path().__withLog(log)
|
|
expect(p1.log()).to.equal('hello')
|
|
})
|
|
|
|
it('Should set hidden to true/false', () => {
|
|
const p1 = new Path().setHidden(true)
|
|
expect(p1.hidden).to.equal(true)
|
|
})
|
|
|
|
it('Should set class with setClass', () => {
|
|
const p1 = new Path().setClass('fabric')
|
|
p1.setClass()
|
|
expect(p1.attributes.get('class')).to.equal('fabric')
|
|
})
|
|
|
|
it('Should log a warning when moving to a non-point', () => {
|
|
let invalid = false
|
|
const log = { warn: () => (invalid = true) }
|
|
const p1 = new Path().__withLog(log)
|
|
expect(invalid).to.equal(false)
|
|
try {
|
|
p1.move('a')
|
|
} catch (err) {
|
|
expect('' + err).to.contain('check is not a function')
|
|
}
|
|
expect(invalid).to.equal(true)
|
|
})
|
|
|
|
it('Should log a warning when drawing a line to a non-point', () => {
|
|
let invalid = false
|
|
const log = { warn: () => (invalid = true) }
|
|
const p1 = new Path().__withLog(log)
|
|
expect(invalid).to.equal(false)
|
|
try {
|
|
p1.line('a')
|
|
} catch (err) {
|
|
expect('' + err).to.contain('check is not a function')
|
|
}
|
|
expect(invalid).to.equal(true)
|
|
})
|
|
|
|
it('Should log a warning when drawing a curve to a non-point', () => {
|
|
let invalid = false
|
|
const log = { warn: () => (invalid = true) }
|
|
const p1 = new Path().__withLog(log)
|
|
const a = new Point(0, 0)
|
|
const b = new Point(10, 10)
|
|
expect(invalid).to.equal(false)
|
|
try {
|
|
p1.move(b).curve(a, b, 'c')
|
|
} catch (err) {
|
|
expect('' + err).to.contain('check is not a function')
|
|
}
|
|
expect(invalid).to.equal(true)
|
|
})
|
|
|
|
it('Should log a warning when drawing a curve with a Cp1 that is a non-point', () => {
|
|
let invalid = false
|
|
const log = { warn: () => (invalid = true) }
|
|
const p1 = new Path().__withLog(log)
|
|
const a = new Point(0, 0)
|
|
const b = new Point(10, 10)
|
|
expect(invalid).to.equal(false)
|
|
try {
|
|
p1.move(b).curve(a, 'x', b)
|
|
} catch (err) {
|
|
expect('' + err).to.contain('check is not a function')
|
|
}
|
|
expect(invalid).to.equal(true)
|
|
})
|
|
|
|
it('Should log a warning when drawing a curve with a Cp1 that is a non-point', () => {
|
|
let invalid = false
|
|
const log = { warn: () => (invalid = true) }
|
|
const p1 = new Path().__withLog(log)
|
|
const b = new Point(10, 10)
|
|
expect(invalid).to.equal(false)
|
|
try {
|
|
p1.move(b).curve('a', b, b)
|
|
} catch (err) {
|
|
expect('' + err).to.contain('copy is not a function')
|
|
}
|
|
expect(invalid).to.equal(true)
|
|
})
|
|
|
|
it('Should log a warning when drawing a curve with a Cp2 that is a non-point', () => {
|
|
let invalid = false
|
|
const log = { warn: () => (invalid = true) }
|
|
const p1 = new Path().__withLog(log)
|
|
const b = new Point(10, 10)
|
|
expect(invalid).to.equal(false)
|
|
try {
|
|
p1.move(b).curve(b, 'a', b)
|
|
} catch (err) {
|
|
expect('' + err).to.contain('copy is not a function')
|
|
}
|
|
expect(invalid).to.equal(true)
|
|
})
|
|
|
|
it('Should log a warning when drawing a _curve with a To that is a non-point', () => {
|
|
let invalid = false
|
|
const log = { warn: () => (invalid = true) }
|
|
const p1 = new Path().__withLog(log)
|
|
const b = new Point(10, 10)
|
|
expect(invalid).to.equal(false)
|
|
try {
|
|
p1.move(b)._curve(b, 'a')
|
|
} catch (err) {
|
|
expect('' + err).to.contain('copy is not a function')
|
|
}
|
|
expect(invalid).to.equal(true)
|
|
})
|
|
|
|
it('Should log a warning when drawing a _curve with a Cp2 that is a non-point', () => {
|
|
let invalid = false
|
|
const log = { warn: () => (invalid = true) }
|
|
const p1 = new Path().__withLog(log)
|
|
const b = new Point(10, 10)
|
|
expect(invalid).to.equal(false)
|
|
try {
|
|
p1.move(b)._curve('a', b)
|
|
} catch (err) {
|
|
expect('' + err).to.contain('copy is not a function')
|
|
}
|
|
expect(invalid).to.equal(true)
|
|
})
|
|
|
|
it('Should log a warning when drawing a curve_ with a To that is a non-point', () => {
|
|
let invalid = false
|
|
const log = { warn: () => (invalid = true) }
|
|
const p1 = new Path().__withLog(log)
|
|
const b = new Point(10, 10)
|
|
expect(invalid).to.equal(false)
|
|
try {
|
|
p1.move(b).curve_(b, 'a')
|
|
} catch (err) {
|
|
expect('' + err).to.contain('copy is not a function')
|
|
}
|
|
expect(invalid).to.equal(true)
|
|
})
|
|
|
|
it('Should log a warning when drawing a curve_ with a Cp2 that is a non-point', () => {
|
|
let invalid = false
|
|
const log = { warn: () => (invalid = true) }
|
|
const p1 = new Path().__withLog(log)
|
|
const b = new Point(10, 10)
|
|
expect(invalid).to.equal(false)
|
|
try {
|
|
p1.move(b).curve_('a', b)
|
|
} catch (err) {
|
|
expect('' + err).to.contain('copy is not a function')
|
|
}
|
|
expect(invalid).to.equal(true)
|
|
})
|
|
|
|
it('Should add a noop operation', () => {
|
|
const p1 = new Path().noop()
|
|
expect(p1.ops.length).to.equal(1)
|
|
expect(p1.ops[0].type).to.equal('noop')
|
|
})
|
|
|
|
it('Should handle an insop operation', () => {
|
|
const a = new Point(0, 0)
|
|
const b = new Point(10, 10)
|
|
const p1 = new Path().move(a).line(b)
|
|
const p2 = new Path().noop('test').insop('test', p1)
|
|
expect(p2.ops.length).to.equal(2)
|
|
expect(p1.ops[0].type).to.equal('move')
|
|
expect(p1.ops[1].type).to.equal('line')
|
|
})
|
|
|
|
it('Should log a warning when an insop operation used an falsy ID', () => {
|
|
let invalid = false
|
|
const log = { warn: () => (invalid = true) }
|
|
const a = new Point(0, 0)
|
|
const b = new Point(10, 10)
|
|
const p1 = new Path().move(a).line(b)
|
|
expect(invalid).to.equal(false)
|
|
new Path().__withLog(log).noop('test').insop(false, p1)
|
|
expect(invalid).to.equal(true)
|
|
})
|
|
|
|
it('Should log a warning when an insop operation used an falsy ID', () => {
|
|
let invalid = false
|
|
const log = { warn: () => (invalid = true) }
|
|
const a = new Point(0, 0)
|
|
const b = new Point(10, 10)
|
|
new Path().move(a).line(b)
|
|
expect(invalid).to.equal(false)
|
|
try {
|
|
new Path().__withLog(log).noop('test').insop('test')
|
|
} catch (err) {
|
|
expect('' + err).to.contain("Cannot read properties of undefined (reading 'ops')")
|
|
}
|
|
expect(invalid).to.equal(true)
|
|
})
|
|
|
|
it('Should log a warning when setting an attribute without a name', () => {
|
|
let invalid = false
|
|
const log = { warn: () => (invalid = true) }
|
|
new Path().__withLog(log).attr()
|
|
expect(invalid).to.equal(true)
|
|
})
|
|
|
|
it('Should log a warning when setting an attribute without a value', () => {
|
|
let invalid = false
|
|
const log = { warn: () => (invalid = true) }
|
|
new Path().__withLog(log).attr('test')
|
|
expect(invalid).to.equal(true)
|
|
})
|
|
|
|
it('Should log an error when calling offset without a distance', () => {
|
|
let invalid = true
|
|
const log = { warn: () => {}, error: () => (invalid = true) }
|
|
const pointLog = { error: () => {} }
|
|
const pointA = new Point(0, 0).__withLog(pointLog)
|
|
const pointB = new Point(0, 40).__withLog(pointLog)
|
|
const a = new Path().__withLog(log).move(pointA).line(pointB)
|
|
a.offset()
|
|
expect(invalid).to.equal(true)
|
|
})
|
|
|
|
it('Should log an error when calling join without a path', () => {
|
|
let invalid = false
|
|
const log = { error: () => (invalid = true) }
|
|
const line = new Path()
|
|
.move(new Point(0, 0))
|
|
.line(new Point(0, 40))
|
|
.attr('class', 'foo')
|
|
.__withLog(log)
|
|
|
|
try {
|
|
line.join()
|
|
} catch (e) {
|
|
expect('' + e).to.contain("TypeError: Cannot read properties of undefined (reading 'ops')")
|
|
} finally {
|
|
expect(invalid).to.equal(true)
|
|
}
|
|
})
|
|
|
|
it('Should log a warning when calling start on a path without drawing operations', () => {
|
|
let invalid = false
|
|
const log = { error: () => (invalid = true) }
|
|
try {
|
|
new Path().__withLog(log).start()
|
|
} catch (err) {
|
|
expect('' + err).to.contain("Cannot read properties of undefined (reading 'to')")
|
|
}
|
|
expect(invalid).to.equal(true)
|
|
})
|
|
|
|
it('Should log an error when calling end on a path without drawing operations', () => {
|
|
let invalid = false
|
|
const log = { error: () => (invalid = true) }
|
|
try {
|
|
new Path().__withLog(log).end()
|
|
} catch (err) {
|
|
expect('' + err).to.contain("Cannot read properties of undefined (reading 'type')")
|
|
}
|
|
expect(invalid).to.equal(true)
|
|
})
|
|
|
|
it('Should log an error when calling shiftAlong but distance is not a number', () => {
|
|
let invalid = false
|
|
const log = { error: () => (invalid = true) }
|
|
expect(invalid).to.equal(false)
|
|
new Path().__withLog(log).move(new Point(0, 0)).line(new Point(10, 10)).shiftAlong()
|
|
expect(invalid).to.equal(true)
|
|
})
|
|
|
|
it('Should log an error when calling shiftFractionalong but fraction is not a number', () => {
|
|
let invalid = false
|
|
const log = { error: () => (invalid = true) }
|
|
new Path().__withLog(log).move(new Point(0, 0)).line(new Point(0, 40)).shiftFractionAlong()
|
|
expect(invalid).to.equal(true)
|
|
})
|
|
|
|
it('Should log an error when splitting a path on a non-point', () => {
|
|
let invalid = false
|
|
const log = { error: () => (invalid = true) }
|
|
const pointLog = { warn: () => {} }
|
|
try {
|
|
new Path()
|
|
.__withLog(log)
|
|
.move(new Point(0, 0).__withLog(pointLog))
|
|
.line(new Point(0, 40).__withLog(pointLog))
|
|
.split()
|
|
} catch (e) {
|
|
expect(e.toString()).to.include(
|
|
"TypeError: Cannot read properties of undefined (reading '__check')"
|
|
)
|
|
} finally {
|
|
expect(invalid).to.equal(true)
|
|
}
|
|
})
|
|
|
|
it('Should add a class', () => {
|
|
const line = new Path().move(new Point(0, 0)).line(new Point(10, 10)).addClass('fabric banana')
|
|
|
|
expect(line.attributes.get('class')).to.equal('fabric banana')
|
|
})
|
|
|
|
it('Should (un)hide a path with hide()/unhide()', () => {
|
|
const path = new Path()
|
|
expect(path.hidden).to.equal(false)
|
|
path.hide()
|
|
expect(path.hidden).to.equal(true)
|
|
path.unhide()
|
|
expect(path.hidden).to.equal(false)
|
|
})
|
|
})
|