@triforge/curve-core 0.1.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/dist/core/BaseCurve.d.ts +44 -0
- package/dist/core/BaseCurve.d.ts.map +1 -0
- package/dist/core/BaseCurve.js +83 -0
- package/dist/core/BaseCurve.js.map +1 -0
- package/dist/core/BezierCurve.d.ts +63 -0
- package/dist/core/BezierCurve.d.ts.map +1 -0
- package/dist/core/BezierCurve.js +218 -0
- package/dist/core/BezierCurve.js.map +1 -0
- package/dist/core/CatmullRomCurve.d.ts +28 -0
- package/dist/core/CatmullRomCurve.d.ts.map +1 -0
- package/dist/core/CatmullRomCurve.js +100 -0
- package/dist/core/CatmullRomCurve.js.map +1 -0
- package/dist/core/NURBSCurve.d.ts +42 -0
- package/dist/core/NURBSCurve.d.ts.map +1 -0
- package/dist/core/NURBSCurve.js +103 -0
- package/dist/core/NURBSCurve.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/operations/CurveBevel.d.ts +43 -0
- package/dist/operations/CurveBevel.d.ts.map +1 -0
- package/dist/operations/CurveBevel.js +140 -0
- package/dist/operations/CurveBevel.js.map +1 -0
- package/dist/operations/CurveLine.d.ts +19 -0
- package/dist/operations/CurveLine.d.ts.map +1 -0
- package/dist/operations/CurveLine.js +25 -0
- package/dist/operations/CurveLine.js.map +1 -0
- package/dist/operations/CurveTube.d.ts +36 -0
- package/dist/operations/CurveTube.d.ts.map +1 -0
- package/dist/operations/CurveTube.js +141 -0
- package/dist/operations/CurveTube.js.map +1 -0
- package/dist/operations/PathFollow.d.ts +53 -0
- package/dist/operations/PathFollow.d.ts.map +1 -0
- package/dist/operations/PathFollow.js +84 -0
- package/dist/operations/PathFollow.js.map +1 -0
- package/dist/utils/frames.d.ts +30 -0
- package/dist/utils/frames.d.ts.map +1 -0
- package/dist/utils/frames.js +77 -0
- package/dist/utils/frames.js.map +1 -0
- package/package.json +45 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Object3D, Matrix4, Vector3, Quaternion } from 'three';
|
|
2
|
+
import type { BaseCurve } from '../core/BaseCurve.js';
|
|
3
|
+
/**
|
|
4
|
+
* PathFollow — drives an Object3D along a curve.
|
|
5
|
+
* Blender: Follow Path constraint / Curve modifier in path mode.
|
|
6
|
+
*
|
|
7
|
+
* Precomputes rotation-minimizing frames and lets you:
|
|
8
|
+
* - Place an object at any arc-length fraction u ∈ [0,1]
|
|
9
|
+
* - Get the 4×4 transform matrix at any u
|
|
10
|
+
* - Animate the `parameters.offset` with st-keyframe for path animation
|
|
11
|
+
*/
|
|
12
|
+
export declare class PathFollow {
|
|
13
|
+
/** All scalar inputs — animate `offset` with st-keyframe. */
|
|
14
|
+
parameters: {
|
|
15
|
+
/** Arc-length fraction: 0 = start, 1 = end. */
|
|
16
|
+
offset: number;
|
|
17
|
+
/** Roll angle in radians added on top of RMF. Default 0. */
|
|
18
|
+
roll: number;
|
|
19
|
+
};
|
|
20
|
+
private _frames;
|
|
21
|
+
private _lut;
|
|
22
|
+
private _curve;
|
|
23
|
+
constructor(curve: BaseCurve, opts?: {
|
|
24
|
+
frameCount?: number;
|
|
25
|
+
offset?: number;
|
|
26
|
+
roll?: number;
|
|
27
|
+
});
|
|
28
|
+
/** Interpolate a frame at arc-length fraction u ∈ [0,1]. */
|
|
29
|
+
private _frameAt;
|
|
30
|
+
/**
|
|
31
|
+
* Get the 4×4 transform matrix at arc-length fraction u.
|
|
32
|
+
* If u is omitted, uses `parameters.offset`.
|
|
33
|
+
*/
|
|
34
|
+
getMatrix(u?: number, out?: Matrix4): Matrix4;
|
|
35
|
+
/**
|
|
36
|
+
* Get position at arc-length fraction u.
|
|
37
|
+
*/
|
|
38
|
+
getPosition(u?: number, out?: Vector3): Vector3;
|
|
39
|
+
/**
|
|
40
|
+
* Get rotation quaternion at arc-length fraction u.
|
|
41
|
+
*/
|
|
42
|
+
getQuaternion(u?: number, out?: Quaternion): Quaternion;
|
|
43
|
+
/**
|
|
44
|
+
* Apply the current `parameters.offset` position and orientation to an Object3D.
|
|
45
|
+
* Call this every frame after animating `parameters.offset` with st-keyframe.
|
|
46
|
+
*/
|
|
47
|
+
apply(object: Object3D): void;
|
|
48
|
+
/** Total arc length of the curve. */
|
|
49
|
+
get length(): number;
|
|
50
|
+
/** Rebuild frames if the curve's control points changed. */
|
|
51
|
+
rebuild(frameCount?: number): void;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=PathFollow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PathFollow.d.ts","sourceRoot":"","sources":["../../src/operations/PathFollow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAA;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAGrD;;;;;;;;GAQG;AACH,qBAAa,UAAU;IACrB,6DAA6D;IAC7D,UAAU,EAAE;QACV,+CAA+C;QAC/C,MAAM,EAAE,MAAM,CAAA;QACd,4DAA4D;QAC5D,IAAI,EAAE,MAAM,CAAA;KACb,CAAA;IAED,OAAO,CAAC,OAAO,CAAoC;IACnD,OAAO,CAAC,IAAI,CAA8B;IAC1C,OAAO,CAAC,MAAM,CAAW;gBAEb,KAAK,EAAE,SAAS,EAAE,IAAI,GAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAO;IAQhG,4DAA4D;IAC5D,OAAO,CAAC,QAAQ;IAgBhB;;;OAGG;IACH,SAAS,CAAC,CAAC,SAAyB,EAAE,GAAG,UAAgB,GAAG,OAAO;IAcnE;;OAEG;IACH,WAAW,CAAC,CAAC,SAAyB,EAAE,GAAG,UAAgB,GAAG,OAAO;IAIrE;;OAEG;IACH,aAAa,CAAC,CAAC,SAAyB,EAAE,GAAG,aAAmB,GAAG,UAAU;IAI7E;;;OAGG;IACH,KAAK,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI;IAS7B,qCAAqC;IACrC,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,4DAA4D;IAC5D,OAAO,CAAC,UAAU,SAAsB,GAAG,IAAI;CAIhD"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { Matrix4, Vector3, Quaternion } from 'three';
|
|
2
|
+
import { computeRMFrames, frameToMatrix, frameToQuaternion } from '../utils/frames.js';
|
|
3
|
+
/**
|
|
4
|
+
* PathFollow — drives an Object3D along a curve.
|
|
5
|
+
* Blender: Follow Path constraint / Curve modifier in path mode.
|
|
6
|
+
*
|
|
7
|
+
* Precomputes rotation-minimizing frames and lets you:
|
|
8
|
+
* - Place an object at any arc-length fraction u ∈ [0,1]
|
|
9
|
+
* - Get the 4×4 transform matrix at any u
|
|
10
|
+
* - Animate the `parameters.offset` with st-keyframe for path animation
|
|
11
|
+
*/
|
|
12
|
+
export class PathFollow {
|
|
13
|
+
constructor(curve, opts = {}) {
|
|
14
|
+
this._curve = curve;
|
|
15
|
+
const count = opts.frameCount ?? 256;
|
|
16
|
+
this._lut = curve.buildArcLengthLUT(count * 2);
|
|
17
|
+
this._frames = computeRMFrames(curve, count);
|
|
18
|
+
this.parameters = { offset: opts.offset ?? 0, roll: opts.roll ?? 0 };
|
|
19
|
+
}
|
|
20
|
+
/** Interpolate a frame at arc-length fraction u ∈ [0,1]. */
|
|
21
|
+
_frameAt(u) {
|
|
22
|
+
u = Math.max(0, Math.min(1, u));
|
|
23
|
+
const idx = u * (this._frames.length - 1);
|
|
24
|
+
const lo = Math.floor(idx);
|
|
25
|
+
const hi = Math.min(lo + 1, this._frames.length - 1);
|
|
26
|
+
const alpha = idx - lo;
|
|
27
|
+
const a = this._frames[lo], b = this._frames[hi];
|
|
28
|
+
return {
|
|
29
|
+
position: new Vector3().lerpVectors(a.position, b.position, alpha),
|
|
30
|
+
tangent: new Vector3().lerpVectors(a.tangent, b.tangent, alpha).normalize(),
|
|
31
|
+
normal: new Vector3().lerpVectors(a.normal, b.normal, alpha).normalize(),
|
|
32
|
+
binormal: new Vector3().lerpVectors(a.binormal, b.binormal, alpha).normalize(),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Get the 4×4 transform matrix at arc-length fraction u.
|
|
37
|
+
* If u is omitted, uses `parameters.offset`.
|
|
38
|
+
*/
|
|
39
|
+
getMatrix(u = this.parameters.offset, out = new Matrix4()) {
|
|
40
|
+
const frame = this._frameAt(u);
|
|
41
|
+
frameToMatrix(frame, out);
|
|
42
|
+
if (this.parameters.roll !== 0) {
|
|
43
|
+
// Apply roll around the tangent axis
|
|
44
|
+
const rollMat = new Matrix4().makeRotationAxis(frame.tangent, this.parameters.roll);
|
|
45
|
+
out.premultiply(rollMat);
|
|
46
|
+
out.setPosition(frame.position);
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Get position at arc-length fraction u.
|
|
52
|
+
*/
|
|
53
|
+
getPosition(u = this.parameters.offset, out = new Vector3()) {
|
|
54
|
+
return this._frameAt(u).position;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Get rotation quaternion at arc-length fraction u.
|
|
58
|
+
*/
|
|
59
|
+
getQuaternion(u = this.parameters.offset, out = new Quaternion()) {
|
|
60
|
+
return frameToQuaternion(this._frameAt(u), out);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Apply the current `parameters.offset` position and orientation to an Object3D.
|
|
64
|
+
* Call this every frame after animating `parameters.offset` with st-keyframe.
|
|
65
|
+
*/
|
|
66
|
+
apply(object) {
|
|
67
|
+
const frame = this._frameAt(this.parameters.offset);
|
|
68
|
+
object.position.copy(frame.position);
|
|
69
|
+
frameToQuaternion(frame, object.quaternion);
|
|
70
|
+
if (this.parameters.roll !== 0) {
|
|
71
|
+
object.rotateOnAxis(frame.tangent, this.parameters.roll);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/** Total arc length of the curve. */
|
|
75
|
+
get length() {
|
|
76
|
+
return this._lut[this._lut.length - 1].len;
|
|
77
|
+
}
|
|
78
|
+
/** Rebuild frames if the curve's control points changed. */
|
|
79
|
+
rebuild(frameCount = this._frames.length) {
|
|
80
|
+
this._lut = this._curve.buildArcLengthLUT(frameCount * 2);
|
|
81
|
+
this._frames = computeRMFrames(this._curve, frameCount);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=PathFollow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PathFollow.js","sourceRoot":"","sources":["../../src/operations/PathFollow.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Vector3, Matrix4, Quaternion } from 'three';
|
|
2
|
+
import type { BaseCurve } from '../core/BaseCurve.js';
|
|
3
|
+
export interface CurveFrame {
|
|
4
|
+
position: Vector3;
|
|
5
|
+
tangent: Vector3;
|
|
6
|
+
normal: Vector3;
|
|
7
|
+
binormal: Vector3;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Compute rotation-minimizing frames (RMF) along a curve.
|
|
11
|
+
* Avoids the spinning / twisting of Frenet frames at inflection points.
|
|
12
|
+
*
|
|
13
|
+
* Algorithm: Double Reflection Method (Wang et al. 2008).
|
|
14
|
+
* Result: `count` frames evenly distributed along the curve (arc-length uniform).
|
|
15
|
+
*
|
|
16
|
+
* Blender uses a similar algorithm for curve tilt and object path-follow.
|
|
17
|
+
*/
|
|
18
|
+
export declare function computeRMFrames(curve: BaseCurve, count: number, divisions?: number): CurveFrame[];
|
|
19
|
+
/**
|
|
20
|
+
* Convert a CurveFrame to a 4×4 transformation matrix.
|
|
21
|
+
* Column order: normal (X), binormal (Y), tangent (Z), position (W).
|
|
22
|
+
* Suitable for placing objects along a curve path.
|
|
23
|
+
*/
|
|
24
|
+
export declare function frameToMatrix(frame: CurveFrame, out?: Matrix4): Matrix4;
|
|
25
|
+
/**
|
|
26
|
+
* Convert a CurveFrame to a Quaternion rotation.
|
|
27
|
+
* Convenience for Object3D.quaternion assignment.
|
|
28
|
+
*/
|
|
29
|
+
export declare function frameToQuaternion(frame: CurveFrame, out?: Quaternion): Quaternion;
|
|
30
|
+
//# sourceMappingURL=frames.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frames.d.ts","sourceRoot":"","sources":["../../src/utils/frames.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAA;AACpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAErD,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,OAAO,CAAA;IACjB,OAAO,EAAG,OAAO,CAAA;IACjB,MAAM,EAAI,OAAO,CAAA;IACjB,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,SAAM,GAAG,UAAU,EAAE,CAwD9F;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,UAAgB,GAAG,OAAO,CAQ7E;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,aAAmB,GAAG,UAAU,CAGvF"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Vector3, Matrix4, Quaternion } from 'three';
|
|
2
|
+
/**
|
|
3
|
+
* Compute rotation-minimizing frames (RMF) along a curve.
|
|
4
|
+
* Avoids the spinning / twisting of Frenet frames at inflection points.
|
|
5
|
+
*
|
|
6
|
+
* Algorithm: Double Reflection Method (Wang et al. 2008).
|
|
7
|
+
* Result: `count` frames evenly distributed along the curve (arc-length uniform).
|
|
8
|
+
*
|
|
9
|
+
* Blender uses a similar algorithm for curve tilt and object path-follow.
|
|
10
|
+
*/
|
|
11
|
+
export function computeRMFrames(curve, count, divisions = 400) {
|
|
12
|
+
const lut = curve.buildArcLengthLUT(divisions);
|
|
13
|
+
const frames = [];
|
|
14
|
+
// Initial frame: use world-up or a fallback to avoid colinear tangent
|
|
15
|
+
const t0 = curve.getPoint(0);
|
|
16
|
+
const tan = curve.getTangent(0);
|
|
17
|
+
let up = new Vector3(0, 1, 0);
|
|
18
|
+
if (Math.abs(tan.dot(up)) > 0.99)
|
|
19
|
+
up = new Vector3(0, 0, 1);
|
|
20
|
+
let normal = new Vector3().crossVectors(up, tan).normalize();
|
|
21
|
+
let binormal = new Vector3().crossVectors(tan, normal).normalize();
|
|
22
|
+
frames.push({
|
|
23
|
+
position: t0.clone(),
|
|
24
|
+
tangent: tan.clone(),
|
|
25
|
+
normal: normal.clone(),
|
|
26
|
+
binormal: binormal.clone(),
|
|
27
|
+
});
|
|
28
|
+
for (let i = 1; i < count; i++) {
|
|
29
|
+
const u = i / (count - 1);
|
|
30
|
+
const tParam = curve.getUtoTmapping(u, lut);
|
|
31
|
+
const pos = curve.getPoint(tParam);
|
|
32
|
+
const tan2 = curve.getTangent(tParam);
|
|
33
|
+
const prev = frames[frames.length - 1];
|
|
34
|
+
// Double reflection transport
|
|
35
|
+
const v1 = pos.clone().sub(prev.position);
|
|
36
|
+
const c1 = v1.dot(v1);
|
|
37
|
+
let rn = prev.normal.clone();
|
|
38
|
+
let rt = prev.tangent.clone();
|
|
39
|
+
if (c1 > 1e-10) {
|
|
40
|
+
const sc1 = 2 / c1;
|
|
41
|
+
rn.sub(v1.clone().multiplyScalar(sc1 * v1.dot(rn)));
|
|
42
|
+
rt.sub(v1.clone().multiplyScalar(sc1 * v1.dot(rt)));
|
|
43
|
+
}
|
|
44
|
+
const v2 = tan2.clone().sub(rt);
|
|
45
|
+
const c2 = v2.dot(v2);
|
|
46
|
+
const sc2 = c2 > 1e-10 ? 2 / c2 : 0;
|
|
47
|
+
const newNormal = rn.clone().sub(v2.clone().multiplyScalar(sc2 * v2.dot(rn))).normalize();
|
|
48
|
+
const newBinormal = new Vector3().crossVectors(tan2, newNormal).normalize();
|
|
49
|
+
frames.push({
|
|
50
|
+
position: pos.clone(),
|
|
51
|
+
tangent: tan2.clone(),
|
|
52
|
+
normal: newNormal,
|
|
53
|
+
binormal: newBinormal,
|
|
54
|
+
});
|
|
55
|
+
normal = newNormal;
|
|
56
|
+
binormal = newBinormal;
|
|
57
|
+
}
|
|
58
|
+
return frames;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Convert a CurveFrame to a 4×4 transformation matrix.
|
|
62
|
+
* Column order: normal (X), binormal (Y), tangent (Z), position (W).
|
|
63
|
+
* Suitable for placing objects along a curve path.
|
|
64
|
+
*/
|
|
65
|
+
export function frameToMatrix(frame, out = new Matrix4()) {
|
|
66
|
+
const { position: p, tangent: z, normal: x, binormal: y } = frame;
|
|
67
|
+
return out.set(x.x, y.x, z.x, p.x, x.y, y.y, z.y, p.y, x.z, y.z, z.z, p.z, 0, 0, 0, 1);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Convert a CurveFrame to a Quaternion rotation.
|
|
71
|
+
* Convenience for Object3D.quaternion assignment.
|
|
72
|
+
*/
|
|
73
|
+
export function frameToQuaternion(frame, out = new Quaternion()) {
|
|
74
|
+
const m = frameToMatrix(frame);
|
|
75
|
+
return out.setFromRotationMatrix(m);
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=frames.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frames.js","sourceRoot":"","sources":["../../src/utils/frames.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@triforge/curve-core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Blender-matched Bezier/NURBS/Catmull-Rom curves with tube, bevel, and path-follow for Three.js",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"package.json"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"dev": "tsc --watch",
|
|
15
|
+
"test": "node test/run-tests.js",
|
|
16
|
+
"bench": "node bench/run-bench.js"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"curve",
|
|
20
|
+
"bezier",
|
|
21
|
+
"nurbs",
|
|
22
|
+
"spline",
|
|
23
|
+
"three.js",
|
|
24
|
+
"blender"
|
|
25
|
+
],
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"three": ">=0.128.0"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/three": "^0.156.0",
|
|
32
|
+
"three": "^0.156.0",
|
|
33
|
+
"typescript": "^5.3.0"
|
|
34
|
+
},
|
|
35
|
+
"author": "Aftab Ibrahim Kazi",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/AftabIbrahimKazi/triforge.git",
|
|
39
|
+
"directory": "st-curve-core"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://github.com/AftabIbrahimKazi/triforge#readme",
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public"
|
|
44
|
+
}
|
|
45
|
+
}
|