@liammartens/svg-path-properties
Version:
Calculate the length for an SVG path, to use it with node or a Canvas element
478 lines (455 loc) • 13.1 kB
text/typescript
import test from "tape";
import SVGPathProperties from "../src/svg-path-properties";
import { inDelta } from "./inDelta";
test("getPointAtLength testing lineTo", function (test) {
const paths = [
{
path: "M0,50L500,50",
xValues: [0, 100, 200, 300, 400, 500],
yValues: [50, 50, 50, 50, 50, 50],
},
{
path: "M0,50L300,300",
xValues: [
0,
59.999996185302734,
119.99999237060547,
180,
239.99998474121094,
300,
],
yValues: [50, 100, 150, 200, 249.99998474121094, 300],
},
{
path: "M0,50H300",
xValues: [0, 50, 100, 150, 200, 250, 300],
yValues: [50, 50, 50, 50, 50, 50, 50],
},
{
path: "M50,50h300",
xValues: [50, 100, 150, 200, 250, 300, 350],
yValues: [50, 50, 50, 50, 50, 50, 50],
},
{
path: "M50,0V200",
xValues: [50, 50, 50, 50, 50, 50, 50],
yValues: [
0,
33.33333206176758,
66.66666412353516,
100,
133.3333282470703,
166.6666717529297,
200,
],
},
{
path: "M50,10v200",
xValues: [50, 50, 50, 50, 50, 50, 50],
yValues: [
10,
43.33333206176758,
76.66666412353516,
110,
143.3333282470703,
176.6666717529297,
210,
],
},
{
path: "M50,50H300V200H50Z",
xValues: [50, 183.3333282470703, 300, 300, 166.66668701171875, 50, 50],
yValues: [50, 50, 66.66665649414062, 200, 200, 183.33331298828125, 50],
},
];
let properties: SVGPathProperties;
for (let i = 0; i < paths.length; i++) {
properties = new SVGPathProperties(paths[i].path);
for (let j = 0; j < paths[i].xValues.length; j++) {
const position = properties.getPointAtLength(
(j * properties.getTotalLength()) / (paths[i].xValues.length - 1)
);
test.true(inDelta(position.x, paths[i].xValues[j], 0.1));
test.true(inDelta(position.y, paths[i].yValues[j], 0.1));
}
test.deepEqual(
properties.getPointAtLength(10000000),
properties.getPointAtLength(properties.getTotalLength())
);
test.deepEqual(
properties.getPointAtLength(-1),
properties.getPointAtLength(0)
);
}
test.end();
});
test("getPointAtLength testing Quadratic Bézier", function (test) {
const paths = [
{
path: "M200,300 Q400,50 600,300",
xValues: [
200,
255.24655151367188,
321.72381591796875,
400.0000305175781,
478.2762756347656,
544.75341796875,
600,
],
yValues: [
300,
240.47999572753906,
194.14747619628906,
175.0000762939453,
194.1474609375,
240.47999572753906,
300,
],
},
{
path: "M0,100 Q50,-50 100,100 T200,100",
xValues: [
0,
25.60834312438965,
74.3916015625,
99.99996948242188,
125.60824584960938,
174.39163208007812,
200,
],
yValues: [
100,
42.84862518310547,
42.84857940673828,
99.99991607666016,
157.15122985839844,
157.15139770507812,
100,
],
},
{
path: "M0,100 q50,-150 100,0 t100,0",
xValues: [
0,
25.60834312438965,
74.3916015625,
99.99996948242188,
125.60824584960938,
174.39163208007812,
200,
],
yValues: [
100,
42.84862518310547,
42.84857940673828,
99.99991607666016,
157.15122985839844,
157.15139770507812,
100,
],
},
{
path: "M0,100 T200,100",
xValues: [
0,
33.33333206176758,
66.66666412353516,
100,
133.3333282470703,
166.6666717529297,
200,
],
yValues: [100, 100, 100, 100, 100, 100, 100],
},
{
path: "M0,100 Q50,-50 100,100 T200,100 T300,100",
xValues: [
0,
50.00000762939453,
99.99998474121094,
149.9999542236328,
200.0000457763672,
250.00059509277344,
300,
],
yValues: [
100,
25.000080108642578,
99.99996185302734,
174.9999237060547,
99.99983978271484,
25.00008201599121,
100,
],
},
];
let properties: SVGPathProperties;
for (let i = 0; i < paths.length; i++) {
properties = new SVGPathProperties(paths[i].path);
for (let j = 0; j < paths[i].xValues.length; j++) {
const position = properties.getPointAtLength(
(j * properties.getTotalLength()) / (paths[i].xValues.length - 1)
);
test.true(inDelta(position.x, paths[i].xValues[j], 1));
test.true(inDelta(position.y, paths[i].yValues[j], 1));
}
test.deepEqual(
properties.getPointAtLength(10000000),
properties.getPointAtLength(properties.getTotalLength())
);
test.deepEqual(
properties.getPointAtLength(-1),
properties.getPointAtLength(0)
);
}
test.end();
});
test("getPointAtLength testing Cubic Bézier", function (test) {
const paths = [
{
path: "M200,200 C275,100 575,100 500,200",
xValues: [
200,
249.48426818847656,
309.1169738769531,
371.97515869140625,
435.7851257324219,
496.41815185546875,
500.0001220703125,
],
yValues: [
200,
160.3770294189453,
137.765380859375,
126.64154052734375,
126.40363311767578,
144.5059051513672,
199.99981689453125,
],
},
{
path: "M100,200 C100,100 250,100 250,200 S400,300 400,200",
xValues: [
100,
136.8885955810547,
213.11134338378906,
250,
286.88836669921875,
363.11114501953125,
400,
],
yValues: [
200,
134.37181091308594,
134.3717498779297,
199.99984741210938,
265.6280517578125,
265.62835693359375,
200,
],
},
{
path: "M100,200 S400,300 400,200",
xValues: [
100,
152.38723754882812,
205.42906188964844,
259.1198425292969,
313.48455810546875,
367.6199951171875,
400,
],
yValues: [
200,
215.58023071289062,
228.76190185546875,
238.95660400390625,
244.3085174560547,
238.78338623046875,
200,
],
},
{
path:
"M240,100C290,100,240,225,290,200S290,75,340,50S515,100,390,150S215,200,90,150S90,25,140,50S140,175,190,200S190,100,240,100",
xValues: [
240,
315.0015563964844,
441.4165954589844,
240.0000762939453,
38.58317947387695,
164.99853515625,
240,
],
yValues: [
100,
121.3836898803711,
111.11810302734375,
187.49990844726562,
111.11775207519531,
121.38365936279297,
100,
],
},
{
path:
"m240,100c50,0,0,125,50,100s0,-125,50,-150s175,50,50,100s-175,50,-300,0s0,-125,50,-100s0,125,50,150s0,-100,50,-100",
xValues: [
240,
315.0015563964844,
441.4165954589844,
240.0000762939453,
38.58317947387695,
164.99853515625,
240,
],
yValues: [
100,
121.3836898803711,
111.11810302734375,
187.49990844726562,
111.11775207519531,
121.38365936279297,
100,
],
},
];
let properties: SVGPathProperties;
for (let i = 0; i < paths.length; i++) {
properties = new SVGPathProperties(paths[i].path);
for (let j = 0; j < paths[i].xValues.length; j++) {
const position = properties.getPointAtLength(
(j * properties.getTotalLength()) / (paths[i].xValues.length - 1)
);
test.true(inDelta(position.x, paths[i].xValues[j], 1));
test.true(inDelta(position.y, paths[i].yValues[j], 1));
}
test.deepEqual(
properties.getPointAtLength(10000000),
properties.getPointAtLength(properties.getTotalLength())
);
test.deepEqual(
properties.getPointAtLength(-1),
properties.getPointAtLength(0)
);
}
test.end();
});
test("getPointAtLength bug testing", function (test) {
//Testing https://github.com/rveciana/svg-path-properties/issues/1
const properties = new SVGPathProperties(
"M 211.6687111164928,312.6478542077994 C 211.6687111164928,312.6478542077994 211.6687111164928,312.6478542077994 219,293"
);
const pos1 = properties.getPointAtLength(12);
const pos2 = properties.getPointAtLength(11.95);
const pos3 = properties.getPointAtLength(12.05);
test.true(inDelta(pos1.x, pos2.x, 0.1));
test.true(inDelta(pos1.x, pos3.x, 0.1));
test.true(inDelta(pos1.y, pos2.y, 0.1));
test.true(inDelta(pos1.y, pos3.y, 0.1));
test.end();
});
test("Testing getPointAtLength with straigh line bezier curve (bug)", function (test) {
//https://github.com/rveciana/svg-path-properties/issues/4
const pathData = new SVGPathProperties("M500,300Q425,325 350,350");
const pathLen = pathData.getTotalLength();
test.true(inDelta(pathLen, 158.11, 0.1)); //Gave undefined
let pos = pathData.getPointAtLength(0);
test.true(inDelta(pos.x, 500, 0.00001));
test.true(inDelta(pos.y, 300, 0.00001));
pos = pathData.getPointAtLength(pathLen);
test.true(inDelta(pos.x, 350, 0.00001));
test.true(inDelta(pos.y, 350, 0.00001));
test.end();
});
test("Testing with multiple rings", function (test) {
let properties = new SVGPathProperties(
"M100,100h100v100h-100Z m200,0h1v1h-1z"
);
test.deepEqual(properties.getPointAtLength(0), { x: 100, y: 100 });
test.deepEqual(properties.getPointAtLength(401), { x: 301, y: 100 });
properties = new SVGPathProperties("M100,100L200,100 M300,100L400,100");
test.deepEqual(properties.getPointAtLength(0), { x: 100, y: 100 });
test.deepEqual(properties.getPointAtLength(100), { x: 200, y: 100 });
test.deepEqual(properties.getPointAtLength(200), { x: 400, y: 100 });
test.deepEqual(
properties.getPointAtLength(200),
properties.getPointAtLength(500)
);
properties = new SVGPathProperties(
"M100,100 L101,100 M200,0 M500,600 M0,0L1,0L1,1L0,1Z"
);
test.deepEqual(properties.getPointAtLength(0), { x: 100, y: 100 });
test.deepEqual(properties.getPointAtLength(1), { x: 101, y: 100 });
test.deepEqual(properties.getPointAtLength(2), { x: 1, y: 0 });
test.end();
});
test("TestingDegenerated quadratic curves, issue 9", function (test) {
let properties = new SVGPathProperties("M60,20Q60,20 150,20");
test.deepEqual(properties.getPointAtLength(2), { x: 62, y: 20 });
test.equal(properties.getTotalLength(), 90);
properties = new SVGPathProperties("M60,20q0,0 90,0");
test.deepEqual(properties.getPointAtLength(2), { x: 62, y: 20 });
test.equal(properties.getTotalLength(), 90);
test.end();
});
test("Check null path, issue 35", function (test) {
let properties = new SVGPathProperties("M0, 0");
test.deepEqual(properties.getPointAtLength(0), { x: 0, y: 0 });
test.deepEqual(properties.getTangentAtLength(0), { x: 0, y: 0 });
test.deepEqual(properties.getPropertiesAtLength(0), {
x: 0,
y: 0,
tangentX: 0,
tangentY: 0,
});
properties = new SVGPathProperties("");
test.deepEqual(properties.getPointAtLength(0), { x: 0, y: 0 });
test.deepEqual(properties.getTangentAtLength(0), { x: 0, y: 0 });
test.deepEqual(properties.getPropertiesAtLength(0), {
x: 0,
y: 0,
tangentX: 0,
tangentY: 0,
});
properties = new SVGPathProperties("M10, 10");
test.deepEqual(properties.getPointAtLength(0), { x: 10, y: 10 });
test.deepEqual(properties.getTangentAtLength(0), { x: 0, y: 0 });
test.deepEqual(properties.getPropertiesAtLength(0), {
x: 10,
y: 10,
tangentX: 0,
tangentY: 0,
});
test.end();
});
test("TestingDegenerated quadratic curves, issue 43", function (test) {
let properties = new SVGPathProperties(
"M224,32C153,195,69,366,76,544C77,567,97,585,105,606C133,683,137,768,175,840C193,875,225,902,250,932"
);
test.deepEqual(properties.getPointAtLength(300), {
x: 111.7377391058728,
y: 310.0179550672576,
});
test.deepEqual(properties.getTangentAtLength(300), {
x: -0.29770950875462776,
y: 0.9546565080682572,
});
test.deepEqual(properties.getPropertiesAtLength(300), {
x: 111.7377391058728,
y: 310.0179550672576,
tangentX: -0.29770950875462776,
tangentY: 0.9546565080682572,
});
test.end();
});
//https://github.com/rveciana/svg-path-properties/pull/44/files
test("Testing first point of zero length fraction", function (test) {
let properties = new SVGPathProperties("M 0,0 l 0 0 l 10 10");
test.deepEqual(properties.getPointAtLength(0), { x: 0, y: 0 });
properties = new SVGPathProperties("M 1,1 L 1 1");
test.deepEqual(properties.getPointAtLength(0), { x: 1, y: 1 });
properties = new SVGPathProperties("M 1,1 l 0 0");
test.deepEqual(properties.getPointAtLength(0), { x: 1, y: 1 });
test.end();
});