@thi.ng/geom-splines 2.2.65 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- - **Last updated**: 2023-12-03T12:13:31Z
3
+ - **Last updated**: 2023-12-11T10:07:09Z
4
4
  - **Generator**: [thi.ng/monopub](https://thi.ng/monopub)
5
5
 
6
6
  All notable changes to this project will be documented in this file.
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
- * Converts elliptic arc into a 1-4 cubic curve segments, depending on arc's
9
- * angle range.
10
- *
11
- * Partially based on:
12
- * https://github.com/chromium/chromium/blob/develop/third_party/blink/renderer/core/svg/svg_path_parser.cc#L253
13
- *
14
- * @param pos - ellipse center
15
- * @param r - ellipse radii
16
- * @param axis - rotation angle (radians)
17
- * @param start - start angle (radians)
18
- * @param end - end angle (radians)
19
- */
20
- export const cubicFromArc = (pos, r, axis, start, end) => {
21
- const p = pointAtTheta(pos, r, axis, start);
22
- const q = pointAtTheta(pos, r, axis, end);
23
- const delta = end - start;
24
- const [rx, ry] = r;
25
- const [s, c] = sincos(axis);
26
- const dx = (c * (p[0] - q[0])) / 2 + (s * (p[1] - q[1])) / 2;
27
- const dy = (-s * (p[0] - q[0])) / 2 + (c * (p[1] - q[1])) / 2;
28
- if ((Math.abs(delta) < PI && dx === 0 && dy === 0) || magSq2(r) < EPS) {
29
- return [cubicFromLine(p, q)];
30
- }
31
- const mapP = (x, y) => {
32
- x *= rx;
33
- y *= ry;
34
- return [c * x - s * y + pos[0], s * x + c * y + pos[1]];
35
- };
36
- const res = [];
37
- const n = Math.ceil(roundEps(Math.abs(delta) / HALF_PI, 1e-3));
38
- const d = delta / n;
39
- const t = (8 / 6) * Math.tan(0.25 * d);
40
- if (!isFinite(t)) {
41
- return [cubicFromLine(p, q)];
42
- }
43
- for (let i = n, theta = start, sc = sincos(theta); i > 0; i--, theta += d) {
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
- min[i] = Math.min(pa, pd);
17
- max[i] = Math.max(pa, pd);
18
- const k0 = -pa + pb;
19
- const k1 = pa - 2 * pb + pc;
20
- const k2 = -pa + 3 * pb - 3 * pc + pd;
21
- let h = k1 * k1 - k0 * k2;
22
- if (h > 0) {
23
- h = Math.sqrt(h);
24
- const update = (t) => {
25
- if (t > 0 && t < 1) {
26
- const q = mixCubic(pa, pb, pc, pd, t);
27
- min[i] = Math.min(min[i], q);
28
- max[i] = Math.max(max[i], q);
29
- }
30
- };
31
- update(k0 / (-k1 - h));
32
- update(k0 / (-k1 + h));
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
- export const cubicBounds = (a, b, c, d) => {
36
- const min = [];
37
- const max = [];
38
- for (let i = a.length; i-- > 0;) {
39
- axisBounds(min, max, i, a[i], b[i], c[i], d[i]);
40
- }
41
- return [min, max];
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
  };
@@ -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
- * Performs recursive search for closest point to `p` on cubic curve defined by
6
- * control points `a`,`b`,`c`,`d`. The `res` and `recur` params are used to
7
- * control the recursion behavior. If `eps` is given, the search is terminated
8
- * as soon as a point with a shorter *squared* distance than `eps` is found.
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
- 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;
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
- export 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(null, perpendicularCW(null, cornerBisector([], a, b, c)), corner2(a, b, c));
26
- tangents.push([set2([], b), n]);
27
- }
28
- tangents.push(tangents[0]);
29
- return buildSegments(tangents, t, uniform);
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
- export const openCubicFromBreakPoints = (points, t = 1 / 3, uniform = false) => {
32
- const tangents = [
33
- [points[0], direction2([], points[0], points[1])],
34
- ];
35
- const num = points.length - 1;
36
- for (let i = 1; i < num; i++) {
37
- const a = points[i - 1];
38
- const b = points[i];
39
- const c = points[i + 1];
40
- const n = mulN2(null, perpendicularCW(null, cornerBisector([], a, b, c)), corner2(a, b, c));
41
- tangents.push([set2([], b), n]);
42
- }
43
- tangents.push([points[num], direction2([], points[num - 1], points[num])]);
44
- return buildSegments(tangents, t, uniform);
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
- 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;
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
- 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;
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
- export 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);
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
- export 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);
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
- * Converts line segment `a` -> `b` into a cubic curve, which when
5
- * sampled yields uniformly spaced points. The inner control points are
6
- * located at 1/3 and 2/3 respectively.
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
+ };
@@ -1,40 +1,21 @@
1
1
  import { mixN } from "@thi.ng/vectors/mixn";
2
2
  import { set } from "@thi.ng/vectors/set";
3
- /**
4
- * Converts the quadratic curve defined by given control points into a cubic
5
- * bezier. Returns array of 4 new control points.
6
- *
7
- * @param a
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
- * Splits given cubic curve (defined by given control points) into 2 quadratic
19
- * curve segments approximating the original curve. The `gamma` param (in [0..1]
20
- * interval) can be used to control the split point (default: 0.5). Returns
21
- * array of new curves (each a 3-tuple of control points).
22
- *
23
- * @remarks
24
- * Reference: https://ttnghia.github.io/pdf/QuadraticApproximation.pdf
25
- *
26
- * @param a
27
- * @param b
28
- * @param c
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
- export 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
- }
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
- export const sampleCubicArray = __sampleArray(sampleCubic);
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
- export const cubicSplitAt = (a, b, c, d, t) => {
7
- if (t <= 0 || t >= 1) {
8
- const p = t <= 0 ? a : d;
9
- const c1 = [set([], p), set([], p), set([], p), set([], p)];
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
- ];
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
- export 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(out, addW4(out, a, b, c, d, -3 * ss, 3 * (ss - ts2), 3 * (ts2 - tt), 3 * tt), len);
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
  };
@@ -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
- export const __sample = (sample) => function $(pts, opts) {
7
- if (isPlainObject(opts) && opts.dist !== undefined) {
8
- return new Sampler($(pts, opts.num || DEFAULT_SAMPLES)).sampleUniform(opts.dist, opts.last !== false);
9
- }
10
- opts = isNumber(opts)
11
- ? {
12
- num: opts,
13
- last: true,
14
- }
15
- : {
16
- num: DEFAULT_SAMPLES,
17
- ...opts,
18
- };
19
- const res = [];
20
- sample(res, pts, opts.num);
21
- opts.last && res.push(set([], pts[pts.length - 1]));
22
- return res;
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
- export const __sampleArray = (fn) => (segments, closed = false, opts) => {
25
- const _opts = isNumber(opts) ? { num: opts } : opts;
26
- const n = segments.length - 1;
27
- return Array.prototype.concat.apply([], segments.map((seg, i) => fn(seg, { ..._opts, last: !closed && i === n })));
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.65",
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 clean && tsc --declaration",
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,17 +35,17 @@
33
35
  "test": "bun test"
34
36
  },
35
37
  "dependencies": {
36
- "@thi.ng/api": "^8.9.10",
37
- "@thi.ng/checks": "^3.4.10",
38
- "@thi.ng/geom-api": "^3.4.48",
39
- "@thi.ng/geom-arc": "^2.1.91",
40
- "@thi.ng/geom-resample": "^2.3.12",
41
- "@thi.ng/math": "^5.7.5",
42
- "@thi.ng/vectors": "^7.8.7"
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",
46
- "@thi.ng/testament": "^0.4.3",
48
+ "esbuild": "^0.19.8",
47
49
  "rimraf": "^5.0.5",
48
50
  "tools": "^0.0.1",
49
51
  "typedoc": "^0.25.4",
@@ -143,5 +145,5 @@
143
145
  "geom-subdiv-curve"
144
146
  ]
145
147
  },
146
- "gitHead": "04d1de79f256d7a53c6b5fd157b37f49bc88e11d\n"
148
+ "gitHead": "5e7bafedfc3d53bc131469a28de31dd8e5b4a3ff\n"
147
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
- * Same as thi.ng/vectors/mixCubic
5
- */
6
- export const cubicPointAt = mixCubic;
7
- /**
8
- * Same as thi.ng/vectors/mixQuadratic
9
- */
10
- export const quadraticPointAt = mixQuadratic;
3
+ const cubicPointAt = mixCubic;
4
+ const quadraticPointAt = mixQuadratic;
5
+ export {
6
+ cubicPointAt,
7
+ quadraticPointAt
8
+ };
@@ -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
- const t = clamp01((a - b) / (a - 2.0 * b + c));
6
- const s = 1 - t;
7
- return s * s * a + 2.0 * s * t * b + t * t * c;
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, min, max) => {
10
- for (let i = p.length; i-- > 0;) {
11
- if (!inRange(p[i], min[i], max[i]))
12
- return false;
13
- }
14
- return true;
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
- export 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]);
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
- return [mi, ma];
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
- * Performs recursive search for closest point to `p` on quadratic curve defined
6
- * by control points `a`,`b`,`c`. The `res` and `recur` params are used to
7
- * control the recursion behavior. If `eps` is given, the search is terminated
8
- * as soon as a point with a shorter *squared* distance than `eps` is found.
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
- export const quadraticFromLine = (a, b) => [
4
- set([], a),
5
- addmN([], a, b, 0.5),
6
- set([], b),
3
+ const quadraticFromLine = (a, b) => [
4
+ set([], a),
5
+ addmN([], a, b, 0.5),
6
+ set([], b)
7
7
  ];
8
+ export {
9
+ quadraticFromLine
10
+ };
@@ -1,9 +1,13 @@
1
1
  import { mixQuadratic } from "@thi.ng/vectors/mix-quadratic";
2
2
  import { __sample, __sampleArray } from "./internal/sample.js";
3
- export 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
- }
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
- export const sampleQuadraticArray = __sampleArray(sampleQuadratic);
9
+ const sampleQuadraticArray = __sampleArray(sampleQuadratic);
10
+ export {
11
+ sampleQuadratic,
12
+ sampleQuadraticArray
13
+ };
@@ -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
- export const quadraticSplitAt = (a, b, c, t) => {
7
- if (t <= 0 || t >= 1) {
8
- const p = t <= 0 ? a : c;
9
- const c1 = [set([], p), set([], p), set([], p)];
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
- ];
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));
@@ -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
- export const quadraticTangentAt = (out, a, b, c, t, len = 1) => normalize(out, addW2(out, sub(out, b, a), sub([], c, b), 2 * (1 - t), 2 * t), len);
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
+ };