@thi.ng/geom-splines 2.2.66 → 2.2.67
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/CHANGELOG.md +1 -1
- package/cubic-arc.js +37 -47
- package/cubic-bounds.js +28 -38
- package/cubic-closest-point.js +6 -20
- package/cubic-from-breakpoints.js +45 -33
- package/cubic-from-controlpoints.js +42 -38
- package/cubic-line.js +8 -13
- package/cubic-quadratic.js +17 -36
- package/cubic-sample.js +10 -6
- package/cubic-split.js +34 -18
- package/cubic-tangent.js +13 -6
- package/internal/sample.js +33 -21
- package/package.json +13 -10
- package/point-at.js +6 -8
- package/quadratic-bounds.js +23 -20
- package/quadratic-closest-point.js +6 -19
- package/quadratic-line.js +7 -4
- package/quadratic-sample.js +10 -6
- package/quadratic-split.js +30 -15
- package/quadratic-tangent.js +8 -1
package/CHANGELOG.md
CHANGED
package/cubic-arc.js
CHANGED
|
@@ -4,51 +4,41 @@ import { EPS, HALF_PI, PI } from "@thi.ng/math/api";
|
|
|
4
4
|
import { roundEps } from "@thi.ng/math/prec";
|
|
5
5
|
import { magSq2 } from "@thi.ng/vectors/magsq";
|
|
6
6
|
import { cubicFromLine } from "./cubic-line.js";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const [s1, c1] = sc;
|
|
45
|
-
const [s2, c2] = (sc = sincos(theta + d));
|
|
46
|
-
res.push([
|
|
47
|
-
mapP(c1, s1),
|
|
48
|
-
mapP(c1 - s1 * t, s1 + c1 * t),
|
|
49
|
-
mapP(c2 + s2 * t, s2 - c2 * t),
|
|
50
|
-
mapP(c2, s2),
|
|
51
|
-
]);
|
|
52
|
-
}
|
|
53
|
-
return res;
|
|
7
|
+
const cubicFromArc = (pos, r, axis, start, end) => {
|
|
8
|
+
const p = pointAtTheta(pos, r, axis, start);
|
|
9
|
+
const q = pointAtTheta(pos, r, axis, end);
|
|
10
|
+
const delta = end - start;
|
|
11
|
+
const [rx, ry] = r;
|
|
12
|
+
const [s, c] = sincos(axis);
|
|
13
|
+
const dx = c * (p[0] - q[0]) / 2 + s * (p[1] - q[1]) / 2;
|
|
14
|
+
const dy = -s * (p[0] - q[0]) / 2 + c * (p[1] - q[1]) / 2;
|
|
15
|
+
if (Math.abs(delta) < PI && dx === 0 && dy === 0 || magSq2(r) < EPS) {
|
|
16
|
+
return [cubicFromLine(p, q)];
|
|
17
|
+
}
|
|
18
|
+
const mapP = (x, y) => {
|
|
19
|
+
x *= rx;
|
|
20
|
+
y *= ry;
|
|
21
|
+
return [c * x - s * y + pos[0], s * x + c * y + pos[1]];
|
|
22
|
+
};
|
|
23
|
+
const res = [];
|
|
24
|
+
const n = Math.ceil(roundEps(Math.abs(delta) / HALF_PI, 1e-3));
|
|
25
|
+
const d = delta / n;
|
|
26
|
+
const t = 8 / 6 * Math.tan(0.25 * d);
|
|
27
|
+
if (!isFinite(t)) {
|
|
28
|
+
return [cubicFromLine(p, q)];
|
|
29
|
+
}
|
|
30
|
+
for (let i = n, theta = start, sc = sincos(theta); i > 0; i--, theta += d) {
|
|
31
|
+
const [s1, c1] = sc;
|
|
32
|
+
const [s2, c2] = sc = sincos(theta + d);
|
|
33
|
+
res.push([
|
|
34
|
+
mapP(c1, s1),
|
|
35
|
+
mapP(c1 - s1 * t, s1 + c1 * t),
|
|
36
|
+
mapP(c2 + s2 * t, s2 - c2 * t),
|
|
37
|
+
mapP(c2, s2)
|
|
38
|
+
]);
|
|
39
|
+
}
|
|
40
|
+
return res;
|
|
41
|
+
};
|
|
42
|
+
export {
|
|
43
|
+
cubicFromArc
|
|
54
44
|
};
|
package/cubic-bounds.js
CHANGED
|
@@ -1,42 +1,32 @@
|
|
|
1
1
|
import { mixCubic } from "@thi.ng/math/mix";
|
|
2
|
-
/**
|
|
3
|
-
* Computes cubic spline bounds for a single vector component.
|
|
4
|
-
*
|
|
5
|
-
* Based on: http://www.iquilezles.org/www/articles/bezierbbox/bezierbbox.htm
|
|
6
|
-
*
|
|
7
|
-
* @param min - min accumulator
|
|
8
|
-
* @param max - max accumulator
|
|
9
|
-
* @param i - vector component
|
|
10
|
-
* @param pa - control point 1
|
|
11
|
-
* @param pb - control point 2
|
|
12
|
-
* @param pc - control point 3
|
|
13
|
-
* @param pd - control point 4
|
|
14
|
-
*/
|
|
15
2
|
const axisBounds = (min, max, i, pa, pb, pc, pd) => {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
3
|
+
min[i] = Math.min(pa, pd);
|
|
4
|
+
max[i] = Math.max(pa, pd);
|
|
5
|
+
const k0 = -pa + pb;
|
|
6
|
+
const k1 = pa - 2 * pb + pc;
|
|
7
|
+
const k2 = -pa + 3 * pb - 3 * pc + pd;
|
|
8
|
+
let h = k1 * k1 - k0 * k2;
|
|
9
|
+
if (h > 0) {
|
|
10
|
+
h = Math.sqrt(h);
|
|
11
|
+
const update = (t) => {
|
|
12
|
+
if (t > 0 && t < 1) {
|
|
13
|
+
const q = mixCubic(pa, pb, pc, pd, t);
|
|
14
|
+
min[i] = Math.min(min[i], q);
|
|
15
|
+
max[i] = Math.max(max[i], q);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
update(k0 / (-k1 - h));
|
|
19
|
+
update(k0 / (-k1 + h));
|
|
20
|
+
}
|
|
34
21
|
};
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
22
|
+
const cubicBounds = (a, b, c, d) => {
|
|
23
|
+
const min = [];
|
|
24
|
+
const max = [];
|
|
25
|
+
for (let i = a.length; i-- > 0; ) {
|
|
26
|
+
axisBounds(min, max, i, a[i], b[i], c[i], d[i]);
|
|
27
|
+
}
|
|
28
|
+
return [min, max];
|
|
29
|
+
};
|
|
30
|
+
export {
|
|
31
|
+
cubicBounds
|
|
42
32
|
};
|
package/cubic-closest-point.js
CHANGED
|
@@ -1,24 +1,10 @@
|
|
|
1
1
|
import { minError } from "@thi.ng/math/min-error";
|
|
2
2
|
import { distSq } from "@thi.ng/vectors/distsq";
|
|
3
3
|
import { mixCubic } from "@thi.ng/vectors/mix-cubic";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
* [`minError()`](https://docs.thi.ng/umbrella/math/functions/minError.html)
|
|
11
|
-
*
|
|
12
|
-
* @param p - query point
|
|
13
|
-
* @param a - control point 1
|
|
14
|
-
* @param b - control point 2
|
|
15
|
-
* @param c - control point 3
|
|
16
|
-
* @param d - control point 4
|
|
17
|
-
* @param res - search steps per iteration
|
|
18
|
-
* @param iter - iterations
|
|
19
|
-
* @param eps - epsilon value
|
|
20
|
-
*/
|
|
21
|
-
export const closestPointCubic = (p, a, b, c, d, out = [], res, iter, eps) => {
|
|
22
|
-
const fn = (t) => mixCubic(out, a, b, c, d, t);
|
|
23
|
-
return fn(minError(fn, distSq, p, res, iter, 0, 1, eps));
|
|
4
|
+
const closestPointCubic = (p, a, b, c, d, out = [], res, iter, eps) => {
|
|
5
|
+
const fn = (t) => mixCubic(out, a, b, c, d, t);
|
|
6
|
+
return fn(minError(fn, distSq, p, res, iter, 0, 1, eps));
|
|
7
|
+
};
|
|
8
|
+
export {
|
|
9
|
+
closestPointCubic
|
|
24
10
|
};
|
|
@@ -7,39 +7,51 @@ import { mulN, mulN2 } from "@thi.ng/vectors/muln";
|
|
|
7
7
|
import { perpendicularCW } from "@thi.ng/vectors/perpendicular";
|
|
8
8
|
import { set2 } from "@thi.ng/vectors/set";
|
|
9
9
|
const buildSegments = (tangents, t, uniform) => {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
const res = [];
|
|
11
|
+
for (let i = 0, num = tangents.length - 1; i < num; i++) {
|
|
12
|
+
const [a, na] = tangents[i];
|
|
13
|
+
const [b, nb] = tangents[i + 1];
|
|
14
|
+
const d = uniform ? t : t * dist(a, b);
|
|
15
|
+
res.push([a, maddN2([], na, d, a), maddN2([], nb, -d, b), b]);
|
|
16
|
+
}
|
|
17
|
+
return res;
|
|
18
18
|
};
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
19
|
+
const closedCubicFromBreakPoints = (points, t = 1 / 3, uniform = false) => {
|
|
20
|
+
const tangents = [];
|
|
21
|
+
for (let num = points.length, i = num - 1, j = 0; j < num; i = j, j++) {
|
|
22
|
+
const a = points[i];
|
|
23
|
+
const b = points[j];
|
|
24
|
+
const c = points[(j + 1) % num];
|
|
25
|
+
const n = mulN(
|
|
26
|
+
null,
|
|
27
|
+
perpendicularCW(null, cornerBisector([], a, b, c)),
|
|
28
|
+
corner2(a, b, c)
|
|
29
|
+
);
|
|
30
|
+
tangents.push([set2([], b), n]);
|
|
31
|
+
}
|
|
32
|
+
tangents.push(tangents[0]);
|
|
33
|
+
return buildSegments(tangents, t, uniform);
|
|
30
34
|
};
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
35
|
+
const openCubicFromBreakPoints = (points, t = 1 / 3, uniform = false) => {
|
|
36
|
+
const tangents = [
|
|
37
|
+
[points[0], direction2([], points[0], points[1])]
|
|
38
|
+
];
|
|
39
|
+
const num = points.length - 1;
|
|
40
|
+
for (let i = 1; i < num; i++) {
|
|
41
|
+
const a = points[i - 1];
|
|
42
|
+
const b = points[i];
|
|
43
|
+
const c = points[i + 1];
|
|
44
|
+
const n = mulN2(
|
|
45
|
+
null,
|
|
46
|
+
perpendicularCW(null, cornerBisector([], a, b, c)),
|
|
47
|
+
corner2(a, b, c)
|
|
48
|
+
);
|
|
49
|
+
tangents.push([set2([], b), n]);
|
|
50
|
+
}
|
|
51
|
+
tangents.push([points[num], direction2([], points[num - 1], points[num])]);
|
|
52
|
+
return buildSegments(tangents, t, uniform);
|
|
53
|
+
};
|
|
54
|
+
export {
|
|
55
|
+
closedCubicFromBreakPoints,
|
|
56
|
+
openCubicFromBreakPoints
|
|
45
57
|
};
|
|
@@ -4,46 +4,50 @@ import { direction } from "@thi.ng/vectors/direction";
|
|
|
4
4
|
import { mixN } from "@thi.ng/vectors/mixn";
|
|
5
5
|
import { set } from "@thi.ng/vectors/set";
|
|
6
6
|
const buildUniform = (segments, t) => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
7
|
+
const res = [];
|
|
8
|
+
for (let i = 0, n = segments.length - 2; i < n; i += 2) {
|
|
9
|
+
const a = segments[i];
|
|
10
|
+
const b = segments[i + 1];
|
|
11
|
+
const c = segments[i + 2];
|
|
12
|
+
res.push([
|
|
13
|
+
a,
|
|
14
|
+
add(null, direction([], a, b, t), a),
|
|
15
|
+
add(null, direction([], c, b, t), c),
|
|
16
|
+
c
|
|
17
|
+
]);
|
|
18
|
+
}
|
|
19
|
+
return res;
|
|
20
20
|
};
|
|
21
21
|
const buildNonUniform = (segments, t) => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
const res = [];
|
|
23
|
+
for (let i = 0, n = segments.length - 2; i < n; i += 2) {
|
|
24
|
+
const a = segments[i];
|
|
25
|
+
const b = segments[i + 1];
|
|
26
|
+
const c = segments[i + 2];
|
|
27
|
+
res.push([a, mixN([], a, b, t), mixN([], c, b, t), c]);
|
|
28
|
+
}
|
|
29
|
+
return res;
|
|
30
30
|
};
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
31
|
+
const closedCubicFromControlPoints = (points, t = 1, uniform = false) => {
|
|
32
|
+
const segments = [];
|
|
33
|
+
for (let i = 0, num = points.length; i < num; i++) {
|
|
34
|
+
const q = points[(i + 1) % num];
|
|
35
|
+
segments.push(addmN([], points[i], q, 0.5), set([], q));
|
|
36
|
+
}
|
|
37
|
+
segments.push(segments[0]);
|
|
38
|
+
return uniform ? buildUniform(segments, t) : buildNonUniform(segments, t);
|
|
39
39
|
};
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
40
|
+
const openCubicFromControlPoints = (points, t = 1, uniform = false) => {
|
|
41
|
+
const segments = [set([], points[0]), set([], points[0])];
|
|
42
|
+
const num = points.length - 1;
|
|
43
|
+
for (let i = 0; i < num; i++) {
|
|
44
|
+
const q = points[i + 1];
|
|
45
|
+
segments.push(addmN([], points[i], q, 0.5), set([], q));
|
|
46
|
+
}
|
|
47
|
+
segments.push(set([], points[num]));
|
|
48
|
+
return uniform ? buildUniform(segments, t) : buildNonUniform(segments, t);
|
|
49
|
+
};
|
|
50
|
+
export {
|
|
51
|
+
closedCubicFromControlPoints,
|
|
52
|
+
openCubicFromControlPoints
|
|
49
53
|
};
|
package/cubic-line.js
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import { mixN } from "@thi.ng/vectors/mixn";
|
|
2
2
|
import { set } from "@thi.ng/vectors/set";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
* @param a - line endpoint
|
|
9
|
-
* @param b - line endpoint
|
|
10
|
-
*/
|
|
11
|
-
export const cubicFromLine = (a, b) => [
|
|
12
|
-
set([], a),
|
|
13
|
-
mixN([], a, b, 1 / 3),
|
|
14
|
-
mixN([], b, a, 1 / 3),
|
|
15
|
-
set([], b),
|
|
3
|
+
const cubicFromLine = (a, b) => [
|
|
4
|
+
set([], a),
|
|
5
|
+
mixN([], a, b, 1 / 3),
|
|
6
|
+
mixN([], b, a, 1 / 3),
|
|
7
|
+
set([], b)
|
|
16
8
|
];
|
|
9
|
+
export {
|
|
10
|
+
cubicFromLine
|
|
11
|
+
};
|
package/cubic-quadratic.js
CHANGED
|
@@ -1,40 +1,21 @@
|
|
|
1
1
|
import { mixN } from "@thi.ng/vectors/mixn";
|
|
2
2
|
import { set } from "@thi.ng/vectors/set";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
* @param b
|
|
9
|
-
* @param c
|
|
10
|
-
*/
|
|
11
|
-
export const cubicFromQuadratic = (a, b, c) => [
|
|
12
|
-
set([], a),
|
|
13
|
-
mixN([], a, b, 2 / 3),
|
|
14
|
-
mixN([], c, b, 2 / 3),
|
|
15
|
-
set([], c),
|
|
3
|
+
const cubicFromQuadratic = (a, b, c) => [
|
|
4
|
+
set([], a),
|
|
5
|
+
mixN([], a, b, 2 / 3),
|
|
6
|
+
mixN([], c, b, 2 / 3),
|
|
7
|
+
set([], c)
|
|
16
8
|
];
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
* @param d
|
|
30
|
-
* @param gamma
|
|
31
|
-
*/
|
|
32
|
-
export const quadraticFromCubic = (a, b, c, d, gamma = 0.5) => {
|
|
33
|
-
const qb = mixN([], a, b, 1.5 * gamma);
|
|
34
|
-
const qd = mixN([], d, c, 1.5 * (1 - gamma));
|
|
35
|
-
const qc = mixN([], qb, qd, gamma);
|
|
36
|
-
return [
|
|
37
|
-
[set([], a), qb, qc],
|
|
38
|
-
[set([], qc), qd, set([], d)],
|
|
39
|
-
];
|
|
9
|
+
const quadraticFromCubic = (a, b, c, d, gamma = 0.5) => {
|
|
10
|
+
const qb = mixN([], a, b, 1.5 * gamma);
|
|
11
|
+
const qd = mixN([], d, c, 1.5 * (1 - gamma));
|
|
12
|
+
const qc = mixN([], qb, qd, gamma);
|
|
13
|
+
return [
|
|
14
|
+
[set([], a), qb, qc],
|
|
15
|
+
[set([], qc), qd, set([], d)]
|
|
16
|
+
];
|
|
17
|
+
};
|
|
18
|
+
export {
|
|
19
|
+
cubicFromQuadratic,
|
|
20
|
+
quadraticFromCubic
|
|
40
21
|
};
|
package/cubic-sample.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { mixCubic } from "@thi.ng/vectors/mix-cubic";
|
|
2
2
|
import { __sample, __sampleArray } from "./internal/sample.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
const sampleCubic = __sample((res, [a, b, c, d], num) => {
|
|
4
|
+
const delta = 1 / num;
|
|
5
|
+
for (let t = 0; t < num; t++) {
|
|
6
|
+
res.push(mixCubic([], a, b, c, d, t * delta));
|
|
7
|
+
}
|
|
8
8
|
});
|
|
9
|
-
|
|
9
|
+
const sampleCubicArray = __sampleArray(sampleCubic);
|
|
10
|
+
export {
|
|
11
|
+
sampleCubic,
|
|
12
|
+
sampleCubicArray
|
|
13
|
+
};
|
package/cubic-split.js
CHANGED
|
@@ -3,22 +3,38 @@ import { distSq } from "@thi.ng/vectors/distsq";
|
|
|
3
3
|
import { mixCubic } from "@thi.ng/vectors/mix-cubic";
|
|
4
4
|
import { mixN } from "@thi.ng/vectors/mixn";
|
|
5
5
|
import { set } from "@thi.ng/vectors/set";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
6
|
+
const cubicSplitAt = (a, b, c, d, t) => {
|
|
7
|
+
if (t <= 0 || t >= 1) {
|
|
8
|
+
const p2 = t <= 0 ? a : d;
|
|
9
|
+
const c1 = [set([], p2), set([], p2), set([], p2), set([], p2)];
|
|
10
|
+
const c2 = [set([], a), set([], b), set([], c), set([], d)];
|
|
11
|
+
return t <= 0 ? [c1, c2] : [c2, c1];
|
|
12
|
+
}
|
|
13
|
+
const ab = mixN([], a, b, t);
|
|
14
|
+
const bc = mixN([], b, c, t);
|
|
15
|
+
const cd = mixN([], c, d, t);
|
|
16
|
+
const abc = mixN([], ab, bc, t);
|
|
17
|
+
const bcd = mixN([], bc, cd, t);
|
|
18
|
+
const p = mixN([], abc, bcd, t);
|
|
19
|
+
return [
|
|
20
|
+
[set([], a), ab, abc, set([], p)],
|
|
21
|
+
[p, bcd, cd, set([], d)]
|
|
22
|
+
];
|
|
23
|
+
};
|
|
24
|
+
const splitCubicNearPoint = (p, a, b, c, d, res, iter) => cubicSplitAt(
|
|
25
|
+
a,
|
|
26
|
+
b,
|
|
27
|
+
c,
|
|
28
|
+
d,
|
|
29
|
+
minError(
|
|
30
|
+
(t) => mixCubic([], a, b, c, d, t),
|
|
31
|
+
distSq,
|
|
32
|
+
p,
|
|
33
|
+
res,
|
|
34
|
+
iter
|
|
35
|
+
)
|
|
36
|
+
);
|
|
37
|
+
export {
|
|
38
|
+
cubicSplitAt,
|
|
39
|
+
splitCubicNearPoint
|
|
23
40
|
};
|
|
24
|
-
export const splitCubicNearPoint = (p, a, b, c, d, res, iter) => cubicSplitAt(a, b, c, d, minError((t) => mixCubic([], a, b, c, d, t), distSq, p, res, iter));
|
package/cubic-tangent.js
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { addW4 } from "@thi.ng/vectors/addw";
|
|
2
2
|
import { normalize } from "@thi.ng/vectors/normalize";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
const cubicTangentAt = (out, a, b, c, d, t, len = 1) => {
|
|
4
|
+
const s = 1 - t;
|
|
5
|
+
const ss = s * s;
|
|
6
|
+
const tt = t * t;
|
|
7
|
+
const ts2 = 2 * t * s;
|
|
8
|
+
return normalize(
|
|
9
|
+
out,
|
|
10
|
+
addW4(out, a, b, c, d, -3 * ss, 3 * (ss - ts2), 3 * (ts2 - tt), 3 * tt),
|
|
11
|
+
len
|
|
12
|
+
);
|
|
13
|
+
};
|
|
14
|
+
export {
|
|
15
|
+
cubicTangentAt
|
|
9
16
|
};
|
package/internal/sample.js
CHANGED
|
@@ -3,26 +3,38 @@ import { isPlainObject } from "@thi.ng/checks/is-plain-object";
|
|
|
3
3
|
import { DEFAULT_SAMPLES } from "@thi.ng/geom-api/sample";
|
|
4
4
|
import { Sampler } from "@thi.ng/geom-resample/sampler";
|
|
5
5
|
import { set } from "@thi.ng/vectors/set";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
6
|
+
const __sample = (sample) => function $(pts, opts) {
|
|
7
|
+
if (isPlainObject(opts) && opts.dist !== void 0) {
|
|
8
|
+
return new Sampler(
|
|
9
|
+
$(pts, opts.num || DEFAULT_SAMPLES)
|
|
10
|
+
).sampleUniform(
|
|
11
|
+
opts.dist,
|
|
12
|
+
opts.last !== false
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
opts = isNumber(opts) ? {
|
|
16
|
+
num: opts,
|
|
17
|
+
last: true
|
|
18
|
+
} : {
|
|
19
|
+
num: DEFAULT_SAMPLES,
|
|
20
|
+
...opts
|
|
21
|
+
};
|
|
22
|
+
const res = [];
|
|
23
|
+
sample(res, pts, opts.num);
|
|
24
|
+
opts.last && res.push(set([], pts[pts.length - 1]));
|
|
25
|
+
return res;
|
|
23
26
|
};
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
const __sampleArray = (fn) => (segments, closed = false, opts) => {
|
|
28
|
+
const _opts = isNumber(opts) ? { num: opts } : opts;
|
|
29
|
+
const n = segments.length - 1;
|
|
30
|
+
return Array.prototype.concat.apply(
|
|
31
|
+
[],
|
|
32
|
+
segments.map(
|
|
33
|
+
(seg, i) => fn(seg, { ..._opts, last: !closed && i === n })
|
|
34
|
+
)
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
export {
|
|
38
|
+
__sample,
|
|
39
|
+
__sampleArray
|
|
28
40
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/geom-splines",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.67",
|
|
4
4
|
"description": "nD cubic & quadratic curve analysis, conversion, interpolation, splitting",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -24,7 +24,9 @@
|
|
|
24
24
|
"author": "Karsten Schmidt (https://thi.ng)",
|
|
25
25
|
"license": "Apache-2.0",
|
|
26
26
|
"scripts": {
|
|
27
|
-
"build": "yarn
|
|
27
|
+
"build": "yarn build:esbuild && yarn build:decl",
|
|
28
|
+
"build:decl": "tsc --declaration --emitDeclarationOnly",
|
|
29
|
+
"build:esbuild": "esbuild --format=esm --platform=neutral --target=es2022 --tsconfig=tsconfig.json --outdir=. src/**/*.ts",
|
|
28
30
|
"clean": "rimraf --glob '*.js' '*.d.ts' '*.map' doc internal",
|
|
29
31
|
"doc": "typedoc --excludePrivate --excludeInternal --out doc src/index.ts",
|
|
30
32
|
"doc:ae": "mkdir -p .ae/doc .ae/temp && api-extractor run --local --verbose",
|
|
@@ -33,16 +35,17 @@
|
|
|
33
35
|
"test": "bun test"
|
|
34
36
|
},
|
|
35
37
|
"dependencies": {
|
|
36
|
-
"@thi.ng/api": "^8.9.
|
|
37
|
-
"@thi.ng/checks": "^3.4.
|
|
38
|
-
"@thi.ng/geom-api": "^3.4.
|
|
39
|
-
"@thi.ng/geom-arc": "^2.1.
|
|
40
|
-
"@thi.ng/geom-resample": "^2.3.
|
|
41
|
-
"@thi.ng/math": "^5.7.
|
|
42
|
-
"@thi.ng/vectors": "^7.8.
|
|
38
|
+
"@thi.ng/api": "^8.9.12",
|
|
39
|
+
"@thi.ng/checks": "^3.4.12",
|
|
40
|
+
"@thi.ng/geom-api": "^3.4.50",
|
|
41
|
+
"@thi.ng/geom-arc": "^2.1.93",
|
|
42
|
+
"@thi.ng/geom-resample": "^2.3.14",
|
|
43
|
+
"@thi.ng/math": "^5.7.7",
|
|
44
|
+
"@thi.ng/vectors": "^7.8.9"
|
|
43
45
|
},
|
|
44
46
|
"devDependencies": {
|
|
45
47
|
"@microsoft/api-extractor": "^7.38.3",
|
|
48
|
+
"esbuild": "^0.19.8",
|
|
46
49
|
"rimraf": "^5.0.5",
|
|
47
50
|
"tools": "^0.0.1",
|
|
48
51
|
"typedoc": "^0.25.4",
|
|
@@ -142,5 +145,5 @@
|
|
|
142
145
|
"geom-subdiv-curve"
|
|
143
146
|
]
|
|
144
147
|
},
|
|
145
|
-
"gitHead": "
|
|
148
|
+
"gitHead": "5e7bafedfc3d53bc131469a28de31dd8e5b4a3ff\n"
|
|
146
149
|
}
|
package/point-at.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { mixCubic } from "@thi.ng/vectors/mix-cubic";
|
|
2
2
|
import { mixQuadratic } from "@thi.ng/vectors/mix-quadratic";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
*/
|
|
10
|
-
export const quadraticPointAt = mixQuadratic;
|
|
3
|
+
const cubicPointAt = mixCubic;
|
|
4
|
+
const quadraticPointAt = mixQuadratic;
|
|
5
|
+
export {
|
|
6
|
+
cubicPointAt,
|
|
7
|
+
quadraticPointAt
|
|
8
|
+
};
|
package/quadratic-bounds.js
CHANGED
|
@@ -2,27 +2,30 @@ import { clamp01, inRange } from "@thi.ng/math/interval";
|
|
|
2
2
|
import { max } from "@thi.ng/vectors/max";
|
|
3
3
|
import { min } from "@thi.ng/vectors/min";
|
|
4
4
|
const solveQuadratic = (a, b, c) => {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
const t = clamp01((a - b) / (a - 2 * b + c));
|
|
6
|
+
const s = 1 - t;
|
|
7
|
+
return s * s * a + 2 * s * t * b + t * t * c;
|
|
8
8
|
};
|
|
9
|
-
const inBounds = (p,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
const inBounds = (p, min2, max2) => {
|
|
10
|
+
for (let i = p.length; i-- > 0; ) {
|
|
11
|
+
if (!inRange(p[i], min2[i], max2[i]))
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
return true;
|
|
15
15
|
};
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
min(null, mi, q);
|
|
25
|
-
max(null, ma, q);
|
|
16
|
+
const quadraticBounds = (a, b, c) => {
|
|
17
|
+
const mi = min([], a, c);
|
|
18
|
+
const ma = max([], a, c);
|
|
19
|
+
if (!inBounds(b, mi, ma)) {
|
|
20
|
+
const q = [];
|
|
21
|
+
for (let i = a.length; i-- > 0; ) {
|
|
22
|
+
q[i] = solveQuadratic(a[i], b[i], c[i]);
|
|
26
23
|
}
|
|
27
|
-
|
|
24
|
+
min(null, mi, q);
|
|
25
|
+
max(null, ma, q);
|
|
26
|
+
}
|
|
27
|
+
return [mi, ma];
|
|
28
|
+
};
|
|
29
|
+
export {
|
|
30
|
+
quadraticBounds
|
|
28
31
|
};
|
|
@@ -1,23 +1,10 @@
|
|
|
1
1
|
import { minError } from "@thi.ng/math/min-error";
|
|
2
2
|
import { distSq } from "@thi.ng/vectors/distsq";
|
|
3
3
|
import { mixQuadratic } from "@thi.ng/vectors/mix-quadratic";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
* [`minError()`](https://docs.thi.ng/umbrella/math/functions/minError.html)
|
|
11
|
-
*
|
|
12
|
-
* @param p - query point
|
|
13
|
-
* @param a - control point 1
|
|
14
|
-
* @param b - control point 2
|
|
15
|
-
* @param c - control point 3
|
|
16
|
-
* @param res - search steps per iteration
|
|
17
|
-
* @param iter - iterations
|
|
18
|
-
* @param eps - epsilon value
|
|
19
|
-
*/
|
|
20
|
-
export const closestPointQuadratic = (p, a, b, c, out = [], res, iter, eps) => {
|
|
21
|
-
const fn = (t) => mixQuadratic(out, a, b, c, t);
|
|
22
|
-
return fn(minError(fn, distSq, p, res, iter, 0, 1, eps));
|
|
4
|
+
const closestPointQuadratic = (p, a, b, c, out = [], res, iter, eps) => {
|
|
5
|
+
const fn = (t) => mixQuadratic(out, a, b, c, t);
|
|
6
|
+
return fn(minError(fn, distSq, p, res, iter, 0, 1, eps));
|
|
7
|
+
};
|
|
8
|
+
export {
|
|
9
|
+
closestPointQuadratic
|
|
23
10
|
};
|
package/quadratic-line.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { addmN } from "@thi.ng/vectors/addmn";
|
|
2
2
|
import { set } from "@thi.ng/vectors/set";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
const quadraticFromLine = (a, b) => [
|
|
4
|
+
set([], a),
|
|
5
|
+
addmN([], a, b, 0.5),
|
|
6
|
+
set([], b)
|
|
7
7
|
];
|
|
8
|
+
export {
|
|
9
|
+
quadraticFromLine
|
|
10
|
+
};
|
package/quadratic-sample.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { mixQuadratic } from "@thi.ng/vectors/mix-quadratic";
|
|
2
2
|
import { __sample, __sampleArray } from "./internal/sample.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
const sampleQuadratic = __sample((res, [a, b, c], num) => {
|
|
4
|
+
const delta = 1 / num;
|
|
5
|
+
for (let t = 0; t < num; t++) {
|
|
6
|
+
res.push(mixQuadratic([], a, b, c, t * delta));
|
|
7
|
+
}
|
|
8
8
|
});
|
|
9
|
-
|
|
9
|
+
const sampleQuadraticArray = __sampleArray(sampleQuadratic);
|
|
10
|
+
export {
|
|
11
|
+
sampleQuadratic,
|
|
12
|
+
sampleQuadraticArray
|
|
13
|
+
};
|
package/quadratic-split.js
CHANGED
|
@@ -3,19 +3,34 @@ import { distSq } from "@thi.ng/vectors/distsq";
|
|
|
3
3
|
import { mixQuadratic } from "@thi.ng/vectors/mix-quadratic";
|
|
4
4
|
import { mixN } from "@thi.ng/vectors/mixn";
|
|
5
5
|
import { set } from "@thi.ng/vectors/set";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
6
|
+
const quadraticSplitAt = (a, b, c, t) => {
|
|
7
|
+
if (t <= 0 || t >= 1) {
|
|
8
|
+
const p2 = t <= 0 ? a : c;
|
|
9
|
+
const c1 = [set([], p2), set([], p2), set([], p2)];
|
|
10
|
+
const c2 = [set([], a), set([], b), set([], c)];
|
|
11
|
+
return t <= 0 ? [c1, c2] : [c2, c1];
|
|
12
|
+
}
|
|
13
|
+
const ab = mixN([], a, b, t);
|
|
14
|
+
const bc = mixN([], b, c, t);
|
|
15
|
+
const p = mixN([], ab, bc, t);
|
|
16
|
+
return [
|
|
17
|
+
[set([], a), ab, p],
|
|
18
|
+
[p, bc, set([], c)]
|
|
19
|
+
];
|
|
20
|
+
};
|
|
21
|
+
const quadraticSplitNearPoint = (p, a, b, c, res, iter) => quadraticSplitAt(
|
|
22
|
+
a,
|
|
23
|
+
b,
|
|
24
|
+
c,
|
|
25
|
+
minError(
|
|
26
|
+
(t) => mixQuadratic([], a, b, c, t),
|
|
27
|
+
distSq,
|
|
28
|
+
p,
|
|
29
|
+
res,
|
|
30
|
+
iter
|
|
31
|
+
)
|
|
32
|
+
);
|
|
33
|
+
export {
|
|
34
|
+
quadraticSplitAt,
|
|
35
|
+
quadraticSplitNearPoint
|
|
20
36
|
};
|
|
21
|
-
export const quadraticSplitNearPoint = (p, a, b, c, res, iter) => quadraticSplitAt(a, b, c, minError((t) => mixQuadratic([], a, b, c, t), distSq, p, res, iter));
|
package/quadratic-tangent.js
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { addW2 } from "@thi.ng/vectors/addw";
|
|
2
2
|
import { normalize } from "@thi.ng/vectors/normalize";
|
|
3
3
|
import { sub } from "@thi.ng/vectors/sub";
|
|
4
|
-
|
|
4
|
+
const quadraticTangentAt = (out, a, b, c, t, len = 1) => normalize(
|
|
5
|
+
out,
|
|
6
|
+
addW2(out, sub(out, b, a), sub([], c, b), 2 * (1 - t), 2 * t),
|
|
7
|
+
len
|
|
8
|
+
);
|
|
9
|
+
export {
|
|
10
|
+
quadraticTangentAt
|
|
11
|
+
};
|