@thi.ng/geom-splines 2.2.105 → 2.3.0

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**: 2024-04-23T07:02:18Z
3
+ - **Last updated**: 2024-06-21T19:34:38Z
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.
@@ -9,6 +9,21 @@ See [Conventional Commits](https://conventionalcommits.org/) for commit guidelin
9
9
  **Note:** Unlisted _patch_ versions only involve non-code or otherwise excluded changes
10
10
  and/or version bumps of transitive dependencies.
11
11
 
12
+ ## [2.3.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/geom-splines@2.3.0) (2024-06-21)
13
+
14
+ #### 🚀 Features
15
+
16
+ - add cubicHobby() curve fitting ([1cefc37](https://github.com/thi-ng/umbrella/commit/1cefc37))
17
+
18
+ #### ⏱ Performance improvements
19
+
20
+ - update cubicHobby2() ([33bb393](https://github.com/thi-ng/umbrella/commit/33bb393))
21
+
22
+ #### ♻️ Refactoring
23
+
24
+ - remove geom-api dep, update imports ([eab7bb9](https://github.com/thi-ng/umbrella/commit/eab7bb9))
25
+ - enforce uniform naming convention of internal functions ([56992b2](https://github.com/thi-ng/umbrella/commit/56992b2))
26
+
12
27
  ### [2.2.61](https://github.com/thi-ng/umbrella/tree/@thi.ng/geom-splines@2.2.61) (2023-11-09)
13
28
 
14
29
  #### ♻️ Refactoring
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  [![Mastodon Follow](https://img.shields.io/mastodon/follow/109331703950160316?domain=https%3A%2F%2Fmastodon.thi.ng&style=social)](https://mastodon.thi.ng/@toxi)
8
8
 
9
9
  > [!NOTE]
10
- > This is one of 192 standalone projects, maintained as part
10
+ > This is one of 193 standalone projects, maintained as part
11
11
  > of the [@thi.ng/umbrella](https://github.com/thi-ng/umbrella/) monorepo
12
12
  > and anti-framework.
13
13
  >
@@ -71,13 +71,12 @@ For Node.js REPL:
71
71
  const gs = await import("@thi.ng/geom-splines");
72
72
  ```
73
73
 
74
- Package sizes (brotli'd, pre-treeshake): ESM: 2.38 KB
74
+ Package sizes (brotli'd, pre-treeshake): ESM: 2.79 KB
75
75
 
76
76
  ## Dependencies
77
77
 
78
78
  - [@thi.ng/api](https://github.com/thi-ng/umbrella/tree/develop/packages/api)
79
79
  - [@thi.ng/checks](https://github.com/thi-ng/umbrella/tree/develop/packages/checks)
80
- - [@thi.ng/geom-api](https://github.com/thi-ng/umbrella/tree/develop/packages/geom-api)
81
80
  - [@thi.ng/geom-arc](https://github.com/thi-ng/umbrella/tree/develop/packages/geom-arc)
82
81
  - [@thi.ng/geom-resample](https://github.com/thi-ng/umbrella/tree/develop/packages/geom-resample)
83
82
  - [@thi.ng/math](https://github.com/thi-ng/umbrella/tree/develop/packages/math)
package/cubic-bounds.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { mixCubic } from "@thi.ng/math/mix";
2
- const axisBounds = (min, max, i, pa, pb, pc, pd) => {
2
+ const __axisBounds = (min, max, i, pa, pb, pc, pd) => {
3
3
  min[i] = Math.min(pa, pd);
4
4
  max[i] = Math.max(pa, pd);
5
5
  const k0 = -pa + pb;
@@ -23,7 +23,7 @@ const cubicBounds = (a, b, c, d) => {
23
23
  const min = [];
24
24
  const max = [];
25
25
  for (let i = a.length; i-- > 0; ) {
26
- axisBounds(min, max, i, a[i], b[i], c[i], d[i]);
26
+ __axisBounds(min, max, i, a[i], b[i], c[i], d[i]);
27
27
  }
28
28
  return [min, max];
29
29
  };
@@ -6,7 +6,7 @@ import { maddN2 } from "@thi.ng/vectors/maddn";
6
6
  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
- const buildSegments = (tangents, t, uniform) => {
9
+ const __buildSegments = (tangents, t, uniform) => {
10
10
  const res = [];
11
11
  for (let i = 0, num = tangents.length - 1; i < num; i++) {
12
12
  const [a, na] = tangents[i];
@@ -30,7 +30,7 @@ const closedCubicFromBreakPoints = (points, t = 1 / 3, uniform = false) => {
30
30
  tangents.push([set2([], b), n]);
31
31
  }
32
32
  tangents.push(tangents[0]);
33
- return buildSegments(tangents, t, uniform);
33
+ return __buildSegments(tangents, t, uniform);
34
34
  };
35
35
  const openCubicFromBreakPoints = (points, t = 1 / 3, uniform = false) => {
36
36
  const tangents = [
@@ -49,7 +49,7 @@ const openCubicFromBreakPoints = (points, t = 1 / 3, uniform = false) => {
49
49
  tangents.push([set2([], b), n]);
50
50
  }
51
51
  tangents.push([points[num], direction2([], points[num - 1], points[num])]);
52
- return buildSegments(tangents, t, uniform);
52
+ return __buildSegments(tangents, t, uniform);
53
53
  };
54
54
  export {
55
55
  closedCubicFromBreakPoints,
@@ -3,7 +3,7 @@ import { addmN } from "@thi.ng/vectors/addmn";
3
3
  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
- const buildUniform = (segments, t) => {
6
+ const __buildUniform = (segments, t) => {
7
7
  const res = [];
8
8
  for (let i = 0, n = segments.length - 2; i < n; i += 2) {
9
9
  const a = segments[i];
@@ -18,7 +18,7 @@ const buildUniform = (segments, t) => {
18
18
  }
19
19
  return res;
20
20
  };
21
- const buildNonUniform = (segments, t) => {
21
+ const __buildNonUniform = (segments, t) => {
22
22
  const res = [];
23
23
  for (let i = 0, n = segments.length - 2; i < n; i += 2) {
24
24
  const a = segments[i];
@@ -35,7 +35,7 @@ const closedCubicFromControlPoints = (points, t = 1, uniform = false) => {
35
35
  segments.push(addmN([], points[i], q, 0.5), set([], q));
36
36
  }
37
37
  segments.push(segments[0]);
38
- return uniform ? buildUniform(segments, t) : buildNonUniform(segments, t);
38
+ return uniform ? __buildUniform(segments, t) : __buildNonUniform(segments, t);
39
39
  };
40
40
  const openCubicFromControlPoints = (points, t = 1, uniform = false) => {
41
41
  const segments = [set([], points[0]), set([], points[0])];
@@ -45,7 +45,7 @@ const openCubicFromControlPoints = (points, t = 1, uniform = false) => {
45
45
  segments.push(addmN([], points[i], q, 0.5), set([], q));
46
46
  }
47
47
  segments.push(set([], points[num]));
48
- return uniform ? buildUniform(segments, t) : buildNonUniform(segments, t);
48
+ return uniform ? __buildUniform(segments, t) : __buildNonUniform(segments, t);
49
49
  };
50
50
  export {
51
51
  closedCubicFromControlPoints,
@@ -0,0 +1,19 @@
1
+ import { type ReadonlyVec, type Vec } from "@thi.ng/vectors";
2
+ /**
3
+ * Fits a cubic bezier spline to the given array of `points` and tension param
4
+ * `omega` (in [0,1] range), using John D. Hobby's algorithm/paper: "Smooth,
5
+ * Easy to Compute Interpolating Splines".
6
+ *
7
+ * @remarks
8
+ * References:
9
+ * - https://link.springer.com/content/pdf/10.1007/BF02187690.pdf
10
+ * - https://web.archive.org/web/20220816011347/https://ctan.math.washington.edu/tex-archive/graphics/pgf/contrib/hobby/hobby.pdf
11
+ * - http://weitz.de/hobby/
12
+ * - https://www.jakelow.com/blog/hobby-curves (based on Weitz)
13
+ *
14
+ * @param points
15
+ * @param closed
16
+ * @param omega
17
+ */
18
+ export declare const cubicHobby2: (points: ReadonlyVec[], closed?: boolean, omega?: number) => Vec[][];
19
+ //# sourceMappingURL=cubic-hobby.d.ts.map
package/cubic-hobby.js ADDED
@@ -0,0 +1,78 @@
1
+ import { THIRD, TWO_THIRD } from "@thi.ng/math/api";
2
+ import { solveTridiagonal } from "@thi.ng/math/solve";
3
+ import {
4
+ angleBetween2,
5
+ mag2
6
+ } from "@thi.ng/vectors";
7
+ import { add2 } from "@thi.ng/vectors/add";
8
+ import { normalize2 } from "@thi.ng/vectors/normalize";
9
+ import { rotate } from "@thi.ng/vectors/rotate";
10
+ import { sub2 } from "@thi.ng/vectors/sub";
11
+ import { set2 } from "@thi.ng/vectors/set";
12
+ const cubicHobby2 = (points, closed = false, omega = 1) => {
13
+ const nump = points.length;
14
+ if (closed) {
15
+ points = points.slice(nump - 3).concat(points, points.slice(0, 3));
16
+ }
17
+ const n = points.length - 1;
18
+ const chords = new Array(n);
19
+ const d = new Array(n);
20
+ const alpha = new Array(n + 1);
21
+ const gamma = new Array(n + 1);
22
+ const A = new Array(n + 1);
23
+ const B = new Array(n + 1);
24
+ const C = new Array(n + 1);
25
+ for (let i = 0; i < n; i++) {
26
+ const delta = chords[i] = sub2([], points[i + 1], points[i]);
27
+ d[i] = mag2(delta);
28
+ i > 0 && (gamma[i] = angleBetween2(chords[i - 1], delta));
29
+ }
30
+ B[0] = B[n] = 2 + omega;
31
+ C[0] = A[n] = 2 * omega + 1;
32
+ alpha[0] = -C[0] * gamma[1];
33
+ alpha[n] = gamma[n] = 0;
34
+ for (let i = 1; i < n; i++) {
35
+ const dprev = d[i - 1];
36
+ const dcurr = d[i];
37
+ const invD = 1 / (dprev * dcurr);
38
+ A[i] = 1 / dprev;
39
+ B[i] = (2 * dprev + 2 * dcurr) * invD;
40
+ C[i] = 1 / dcurr;
41
+ alpha[i] = -(2 * gamma[i] * dcurr + gamma[i + 1] * dprev) * invD;
42
+ }
43
+ solveTridiagonal(A, B, C, alpha);
44
+ const res = [];
45
+ const [from, to] = closed ? [3, 3 + nump] : [0, n];
46
+ for (let i = from; i < to; i++) {
47
+ const a = alpha[i];
48
+ const b = i < n - 1 ? -gamma[i + 1] - alpha[i + 1] : -alpha[n];
49
+ const c = chords[i];
50
+ const cosA = Math.cos(a);
51
+ const cosB = Math.cos(b);
52
+ const scale = TWO_THIRD * d[i];
53
+ res.push([
54
+ set2([], points[i]),
55
+ add2(
56
+ null,
57
+ normalize2(null, rotate([], c, a), __rho(cosA, cosB, scale)),
58
+ points[i]
59
+ ),
60
+ add2(
61
+ null,
62
+ // reuse chord vector for result to avoid extraneous allocation
63
+ normalize2(
64
+ null,
65
+ rotate(null, c, -b),
66
+ -__rho(cosB, cosA, scale)
67
+ ),
68
+ points[i + 1]
69
+ ),
70
+ set2([], points[i + 1])
71
+ ]);
72
+ }
73
+ return res;
74
+ };
75
+ const __rho = (cosA, cosB, scale) => scale / (1 + THIRD * cosA + TWO_THIRD * cosB);
76
+ export {
77
+ cubicHobby2
78
+ };
package/cubic-sample.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export declare const sampleCubic: (pts: import("@thi.ng/vectors").ReadonlyVec[], opts?: number | Partial<import("@thi.ng/geom-api/sample").SamplingOpts> | undefined) => import("@thi.ng/vectors").Vec[];
2
- export declare const sampleCubicArray: (segments: import("@thi.ng/vectors").ReadonlyVec[][], closed: boolean | undefined, opts: number | Partial<import("@thi.ng/geom-api/sample").SamplingOpts>) => any[];
1
+ export declare const sampleCubic: (pts: import("@thi.ng/vectors").ReadonlyVec[], opts?: number | Partial<import("@thi.ng/geom-resample/api").SamplingOpts>) => import("@thi.ng/vectors").Vec[];
2
+ export declare const sampleCubicArray: (segments: import("@thi.ng/vectors").ReadonlyVec[][], closed: boolean | undefined, opts: number | Partial<import("@thi.ng/geom-resample/api").SamplingOpts>) => any[];
3
3
  //# sourceMappingURL=cubic-sample.d.ts.map
package/index.d.ts CHANGED
@@ -3,6 +3,7 @@ export * from "./cubic-bounds.js";
3
3
  export * from "./cubic-closest-point.js";
4
4
  export * from "./cubic-from-breakpoints.js";
5
5
  export * from "./cubic-from-controlpoints.js";
6
+ export * from "./cubic-hobby.js";
6
7
  export * from "./cubic-line.js";
7
8
  export * from "./cubic-quadratic.js";
8
9
  export * from "./cubic-sample.js";
package/index.js CHANGED
@@ -3,6 +3,7 @@ export * from "./cubic-bounds.js";
3
3
  export * from "./cubic-closest-point.js";
4
4
  export * from "./cubic-from-breakpoints.js";
5
5
  export * from "./cubic-from-controlpoints.js";
6
+ export * from "./cubic-hobby.js";
6
7
  export * from "./cubic-line.js";
7
8
  export * from "./cubic-quadratic.js";
8
9
  export * from "./cubic-sample.js";
@@ -1,5 +1,5 @@
1
1
  import type { Fn2, Fn3 } from "@thi.ng/api";
2
- import { type SamplingOpts } from "@thi.ng/geom-api/sample";
2
+ import { type SamplingOpts } from "@thi.ng/geom-resample/api";
3
3
  import type { ReadonlyVec, Vec } from "@thi.ng/vectors";
4
4
  export declare const __sample: (sample: Fn3<Vec[], ReadonlyVec[], number, void>) => (pts: ReadonlyVec[], opts?: number | Partial<SamplingOpts>) => Vec[];
5
5
  export declare const __sampleArray: (fn: Fn2<ReadonlyVec[], Partial<SamplingOpts>, Vec[]>) => (segments: ReadonlyVec[][], closed: boolean | undefined, opts: number | Partial<SamplingOpts>) => any[];
@@ -1,6 +1,6 @@
1
1
  import { isNumber } from "@thi.ng/checks/is-number";
2
2
  import { isPlainObject } from "@thi.ng/checks/is-plain-object";
3
- import { DEFAULT_SAMPLES } from "@thi.ng/geom-api/sample";
3
+ import { DEFAULT_SAMPLES } from "@thi.ng/geom-resample/api";
4
4
  import { Sampler } from "@thi.ng/geom-resample/sampler";
5
5
  import { set } from "@thi.ng/vectors/set";
6
6
  const __sample = (sample) => function $(pts, opts) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/geom-splines",
3
- "version": "2.2.105",
3
+ "version": "2.3.0",
4
4
  "description": "nD cubic & quadratic curve analysis, conversion, interpolation, splitting",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -10,7 +10,7 @@
10
10
  "type": "git",
11
11
  "url": "https://github.com/thi-ng/umbrella.git"
12
12
  },
13
- "homepage": "https://github.com/thi-ng/umbrella/tree/develop/packages/geom-splines#readme",
13
+ "homepage": "https://thi.ng/geom-splines",
14
14
  "funding": [
15
15
  {
16
16
  "type": "github",
@@ -36,19 +36,18 @@
36
36
  "tool:tangle": "../../node_modules/.bin/tangle src/**/*.ts"
37
37
  },
38
38
  "dependencies": {
39
- "@thi.ng/api": "^8.11.1",
40
- "@thi.ng/checks": "^3.6.3",
41
- "@thi.ng/geom-api": "^4.0.9",
42
- "@thi.ng/geom-arc": "^2.1.131",
43
- "@thi.ng/geom-resample": "^2.3.52",
44
- "@thi.ng/math": "^5.10.12",
45
- "@thi.ng/vectors": "^7.10.30"
39
+ "@thi.ng/api": "^8.11.3",
40
+ "@thi.ng/checks": "^3.6.5",
41
+ "@thi.ng/geom-arc": "^2.1.133",
42
+ "@thi.ng/geom-resample": "^3.0.0",
43
+ "@thi.ng/math": "^5.11.0",
44
+ "@thi.ng/vectors": "^7.11.0"
46
45
  },
47
46
  "devDependencies": {
48
- "@microsoft/api-extractor": "^7.43.0",
49
- "esbuild": "^0.20.2",
50
- "typedoc": "^0.25.12",
51
- "typescript": "^5.4.3"
47
+ "@microsoft/api-extractor": "^7.47.0",
48
+ "esbuild": "^0.21.5",
49
+ "typedoc": "^0.25.13",
50
+ "typescript": "^5.5.2"
52
51
  },
53
52
  "keywords": [
54
53
  "2d",
@@ -101,6 +100,9 @@
101
100
  "./cubic-from-controlpoints": {
102
101
  "default": "./cubic-from-controlpoints.js"
103
102
  },
103
+ "./cubic-hobby": {
104
+ "default": "./cubic-hobby.js"
105
+ },
104
106
  "./cubic-line": {
105
107
  "default": "./cubic-line.js"
106
108
  },
@@ -144,5 +146,5 @@
144
146
  "geom-subdiv-curve"
145
147
  ]
146
148
  },
147
- "gitHead": "aed3421c21044c005fbcb7cc37965ccf85a870d2\n"
149
+ "gitHead": "154c95cf9d6bab32174498ec3b5b5d87e42be7f9\n"
148
150
  }
@@ -1,25 +1,24 @@
1
1
  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
- const solveQuadratic = (a, b, c) => {
4
+ const __solveQuadratic = (a, b, c) => {
5
5
  const t = clamp01((a - b) / (a - 2 * b + c));
6
6
  const s = 1 - t;
7
7
  return s * s * a + 2 * s * t * b + t * t * c;
8
8
  };
9
- const inBounds = (p, min2, max2) => {
9
+ const __inBounds = (p, min2, max2) => {
10
10
  for (let i = p.length; i-- > 0; ) {
11
- if (!inRange(p[i], min2[i], max2[i]))
12
- return false;
11
+ if (!inRange(p[i], min2[i], max2[i])) return false;
13
12
  }
14
13
  return true;
15
14
  };
16
15
  const quadraticBounds = (a, b, c) => {
17
16
  const mi = min([], a, c);
18
17
  const ma = max([], a, c);
19
- if (!inBounds(b, mi, ma)) {
18
+ if (!__inBounds(b, mi, ma)) {
20
19
  const q = [];
21
20
  for (let i = a.length; i-- > 0; ) {
22
- q[i] = solveQuadratic(a[i], b[i], c[i]);
21
+ q[i] = __solveQuadratic(a[i], b[i], c[i]);
23
22
  }
24
23
  min(null, mi, q);
25
24
  max(null, ma, q);
@@ -1,3 +1,3 @@
1
- export declare const sampleQuadratic: (pts: import("@thi.ng/vectors").ReadonlyVec[], opts?: number | Partial<import("@thi.ng/geom-api/sample").SamplingOpts> | undefined) => import("@thi.ng/vectors").Vec[];
2
- export declare const sampleQuadraticArray: (segments: import("@thi.ng/vectors").ReadonlyVec[][], closed: boolean | undefined, opts: number | Partial<import("@thi.ng/geom-api/sample").SamplingOpts>) => any[];
1
+ export declare const sampleQuadratic: (pts: import("@thi.ng/vectors").ReadonlyVec[], opts?: number | Partial<import("@thi.ng/geom-resample/api").SamplingOpts>) => import("@thi.ng/vectors").Vec[];
2
+ export declare const sampleQuadraticArray: (segments: import("@thi.ng/vectors").ReadonlyVec[][], closed: boolean | undefined, opts: number | Partial<import("@thi.ng/geom-resample/api").SamplingOpts>) => any[];
3
3
  //# sourceMappingURL=quadratic-sample.d.ts.map