@ue-too/curve 0.14.0 → 0.15.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/README.md CHANGED
@@ -25,11 +25,13 @@ Bezier curve and geometric path library for TypeScript canvas applications.
25
25
  ## Installation
26
26
 
27
27
  Using Bun:
28
+
28
29
  ```bash
29
30
  bun add @ue-too/curve
30
31
  ```
31
32
 
32
33
  Using npm:
34
+
33
35
  ```bash
34
36
  npm install @ue-too/curve
35
37
  ```
@@ -43,9 +45,9 @@ import { BCurve } from '@ue-too/curve';
43
45
 
44
46
  // Create a quadratic Bezier curve
45
47
  const curve = new BCurve([
46
- { x: 0, y: 0 }, // Start point
47
- { x: 50, y: 100 }, // Control point
48
- { x: 100, y: 0 } // End point
48
+ { x: 0, y: 0 }, // Start point
49
+ { x: 50, y: 100 }, // Control point
50
+ { x: 100, y: 0 }, // End point
49
51
  ]);
50
52
 
51
53
  // Evaluate at midpoint (t = 0.5)
@@ -72,23 +74,23 @@ Bezier curves are parametric curves defined by control points. This library supp
72
74
  ```typescript
73
75
  // Linear Bezier (line)
74
76
  const line = new BCurve([
75
- { x: 0, y: 0 },
76
- { x: 100, y: 100 }
77
+ { x: 0, y: 0 },
78
+ { x: 100, y: 100 },
77
79
  ]);
78
80
 
79
81
  // Quadratic Bezier
80
82
  const quadratic = new BCurve([
81
- { x: 0, y: 0 },
82
- { x: 50, y: 100 },
83
- { x: 100, y: 0 }
83
+ { x: 0, y: 0 },
84
+ { x: 50, y: 100 },
85
+ { x: 100, y: 0 },
84
86
  ]);
85
87
 
86
88
  // Cubic Bezier
87
89
  const cubic = new BCurve([
88
- { x: 0, y: 0 },
89
- { x: 33, y: 100 },
90
- { x: 66, y: 100 },
91
- { x: 100, y: 0 }
90
+ { x: 0, y: 0 },
91
+ { x: 33, y: 100 },
92
+ { x: 66, y: 100 },
93
+ { x: 100, y: 0 },
92
94
  ]);
93
95
  ```
94
96
 
@@ -97,9 +99,9 @@ const cubic = new BCurve([
97
99
  Bezier curves are evaluated using a parameter `t` from 0.0 (start) to 1.0 (end):
98
100
 
99
101
  ```typescript
100
- const start = curve.get(0); // Start point
101
- const mid = curve.get(0.5); // Midpoint
102
- const end = curve.get(1); // End point
102
+ const start = curve.get(0); // Start point
103
+ const mid = curve.get(0.5); // Midpoint
104
+ const end = curve.get(1); // End point
103
105
  const quarter = curve.get(0.25); // 25% along the curve
104
106
  ```
105
107
 
@@ -110,38 +112,45 @@ const quarter = curve.get(0.25); // 25% along the curve
110
112
  Main Bezier curve class with extensive geometric operations.
111
113
 
112
114
  **Constructor:**
115
+
113
116
  ```typescript
114
117
  const curve = new BCurve(controlPoints: Point[]);
115
118
  ```
116
119
 
117
120
  **Curve Evaluation:**
121
+
118
122
  - `get(t: number): Point` - Get point at parameter t
119
123
  - `derivative(t: number): Point` - Get derivative vector at t
120
124
  - `normal(t: number): Point` - Get normal vector at t
121
125
  - `tangent(t: number): Point` - Get tangent vector at t
122
126
 
123
127
  **Geometric Properties:**
128
+
124
129
  - `fullLength: number` - Total arc length (cached)
125
130
  - `bbox(): {x: {min: number, max: number}, y: {min: number, max: number}}` - Axis-aligned bounding box
126
131
  - `extrema(): {x: number[], y: number[]}` - Find extrema (min/max) points
127
132
  - `curvature(t: number): number` - Calculate curvature at t
128
133
 
129
134
  **Curve Manipulation:**
135
+
130
136
  - `splitIntoCurves(t: number): [BCurve, BCurve]` - Split into two curves at t
131
137
  - `scale(factor: number, origin?: Point): BCurve` - Scale curve around origin
132
138
  - `offset(distance: number): BCurve | BCurve[]` - Create offset (parallel) curve
133
139
 
134
140
  **Intersection Detection:**
141
+
135
142
  - `getCurveIntersections(other: BCurve): {selfT: number, otherT: number}[]` - Find curve-curve intersections
136
143
  - `getLineIntersections(line: Line): number[]` - Find curve-line intersection points
137
144
  - `getCircleIntersections(center: Point, radius: number): number[]` - Find curve-circle intersections
138
145
  - `getSelfIntersections(): {t1: number, t2: number}[]` - Detect self-intersections
139
146
 
140
147
  **Point Queries:**
148
+
141
149
  - `project(point: Point): {t: number, point: Point, distance: number}` - Project point onto curve
142
150
  - `closestPoint(point: Point): Point` - Find closest point on curve
143
151
 
144
152
  **Arc-Length Functions:**
153
+
145
154
  - `length(t: number): number` - Arc length from start to parameter t
146
155
  - `parameter(length: number): number` - Find parameter t for a given arc length
147
156
 
@@ -150,16 +159,19 @@ const curve = new BCurve(controlPoints: Point[]);
150
159
  Straight line segment utilities.
151
160
 
152
161
  **Constructor:**
162
+
153
163
  ```typescript
154
164
  const line = new Line(start: Point, end: Point);
155
165
  ```
156
166
 
157
167
  **Properties:**
168
+
158
169
  - `start: Point` - Starting point
159
170
  - `end: Point` - Ending point
160
171
  - `length: number` - Line length
161
172
 
162
173
  **Methods:**
174
+
163
175
  - `get(t: number): Point` - Get point at parameter t (0-1)
164
176
  - `intersects(other: Line): Point | null` - Find intersection point with another line
165
177
  - `project(point: Point): {t: number, point: Point}` - Project point onto line
@@ -170,16 +182,18 @@ const line = new Line(start: Point, end: Point);
170
182
  Composite curve made of multiple Bezier segments with control points and tangent handles.
171
183
 
172
184
  **Constructor:**
185
+
173
186
  ```typescript
174
187
  const composite = new CompositeBCurve(controlPoints: ControlPoint[]);
175
188
  ```
176
189
 
177
190
  **Control Point Structure:**
191
+
178
192
  ```typescript
179
193
  type ControlPoint = {
180
- point: Point; // Anchor point
181
- leftHandle?: Point; // Left tangent handle
182
- rightHandle?: Point; // Right tangent handle
194
+ point: Point; // Anchor point
195
+ leftHandle?: Point; // Left tangent handle
196
+ rightHandle?: Point; // Right tangent handle
183
197
  };
184
198
  ```
185
199
 
@@ -188,11 +202,13 @@ type ControlPoint = {
188
202
  Path composed of sequential line segments.
189
203
 
190
204
  **Constructor:**
205
+
191
206
  ```typescript
192
207
  const path = new Path(points: Point[]);
193
208
  ```
194
209
 
195
210
  **Methods:**
211
+
196
212
  - `get(index: number): Line` - Get line segment at index
197
213
  - `length(): number` - Total path length
198
214
  - `bbox(): {x: {min, max}, y: {min, max}}` - Bounding box
@@ -205,9 +221,9 @@ const path = new Path(points: Point[]);
205
221
  import { BCurve } from '@ue-too/curve';
206
222
 
207
223
  const curve = new BCurve([
208
- { x: 50, y: 200 },
209
- { x: 150, y: 50 },
210
- { x: 250, y: 200 }
224
+ { x: 50, y: 200 },
225
+ { x: 150, y: 50 },
226
+ { x: 250, y: 200 },
211
227
  ]);
212
228
 
213
229
  // Draw using canvas API
@@ -215,18 +231,23 @@ ctx.beginPath();
215
231
  ctx.moveTo(curve.points[0].x, curve.points[0].y);
216
232
 
217
233
  if (curve.points.length === 3) {
218
- // Quadratic curve
219
- ctx.quadraticCurveTo(
220
- curve.points[1].x, curve.points[1].y,
221
- curve.points[2].x, curve.points[2].y
222
- );
234
+ // Quadratic curve
235
+ ctx.quadraticCurveTo(
236
+ curve.points[1].x,
237
+ curve.points[1].y,
238
+ curve.points[2].x,
239
+ curve.points[2].y
240
+ );
223
241
  } else if (curve.points.length === 4) {
224
- // Cubic curve
225
- ctx.bezierCurveTo(
226
- curve.points[1].x, curve.points[1].y,
227
- curve.points[2].x, curve.points[2].y,
228
- curve.points[3].x, curve.points[3].y
229
- );
242
+ // Cubic curve
243
+ ctx.bezierCurveTo(
244
+ curve.points[1].x,
245
+ curve.points[1].y,
246
+ curve.points[2].x,
247
+ curve.points[2].y,
248
+ curve.points[3].x,
249
+ curve.points[3].y
250
+ );
230
251
  }
231
252
 
232
253
  ctx.stroke();
@@ -238,25 +259,25 @@ ctx.stroke();
238
259
  import { BCurve } from '@ue-too/curve';
239
260
 
240
261
  const curve = new BCurve([
241
- { x: 0, y: 100 },
242
- { x: 100, y: 0 },
243
- { x: 200, y: 100 }
262
+ { x: 0, y: 100 },
263
+ { x: 100, y: 0 },
264
+ { x: 200, y: 100 },
244
265
  ]);
245
266
 
246
267
  let t = 0;
247
268
 
248
269
  function animate() {
249
- t += 0.01;
250
- if (t > 1) t = 0;
270
+ t += 0.01;
271
+ if (t > 1) t = 0;
251
272
 
252
- const position = curve.get(t);
253
- const tangent = curve.tangent(t);
273
+ const position = curve.get(t);
274
+ const tangent = curve.tangent(t);
254
275
 
255
- // Draw object at position with rotation from tangent
256
- const angle = Math.atan2(tangent.y, tangent.x);
257
- drawSprite(position.x, position.y, angle);
276
+ // Draw object at position with rotation from tangent
277
+ const angle = Math.atan2(tangent.y, tangent.x);
278
+ drawSprite(position.x, position.y, angle);
258
279
 
259
- requestAnimationFrame(animate);
280
+ requestAnimationFrame(animate);
260
281
  }
261
282
  ```
262
283
 
@@ -266,9 +287,9 @@ function animate() {
266
287
  import { BCurve } from '@ue-too/curve';
267
288
 
268
289
  const curve = new BCurve([
269
- { x: 0, y: 0 },
270
- { x: 100, y: 100 },
271
- { x: 200, y: 0 }
290
+ { x: 0, y: 0 },
291
+ { x: 100, y: 100 },
292
+ { x: 200, y: 0 },
272
293
  ]);
273
294
 
274
295
  const totalLength = curve.fullLength;
@@ -277,11 +298,11 @@ const numPoints = Math.floor(totalLength / spacing);
277
298
 
278
299
  // Place points uniformly along the curve
279
300
  for (let i = 0; i <= numPoints; i++) {
280
- const arcLength = i * spacing;
281
- const t = curve.parameter(arcLength); // Convert arc-length to parameter
282
- const point = curve.get(t);
301
+ const arcLength = i * spacing;
302
+ const t = curve.parameter(arcLength); // Convert arc-length to parameter
303
+ const point = curve.get(t);
283
304
 
284
- drawPoint(point.x, point.y);
305
+ drawPoint(point.x, point.y);
285
306
  }
286
307
  ```
287
308
 
@@ -291,25 +312,25 @@ for (let i = 0; i <= numPoints; i++) {
291
312
  import { BCurve } from '@ue-too/curve';
292
313
 
293
314
  const curve1 = new BCurve([
294
- { x: 0, y: 50 },
295
- { x: 100, y: 150 },
296
- { x: 200, y: 50 }
315
+ { x: 0, y: 50 },
316
+ { x: 100, y: 150 },
317
+ { x: 200, y: 50 },
297
318
  ]);
298
319
 
299
320
  const curve2 = new BCurve([
300
- { x: 0, y: 100 },
301
- { x: 100, y: 0 },
302
- { x: 200, y: 100 }
321
+ { x: 0, y: 100 },
322
+ { x: 100, y: 0 },
323
+ { x: 200, y: 100 },
303
324
  ]);
304
325
 
305
326
  const intersections = curve1.getCurveIntersections(curve2);
306
327
 
307
328
  intersections.forEach(({ selfT, otherT }) => {
308
- const point1 = curve1.get(selfT);
309
- const point2 = curve2.get(otherT);
329
+ const point1 = curve1.get(selfT);
330
+ const point2 = curve2.get(otherT);
310
331
 
311
- console.log('Intersection at:', point1);
312
- // point1 and point2 should be very close (within numerical precision)
332
+ console.log('Intersection at:', point1);
333
+ // point1 and point2 should be very close (within numerical precision)
313
334
  });
314
335
  ```
315
336
 
@@ -319,9 +340,9 @@ intersections.forEach(({ selfT, otherT }) => {
319
340
  import { BCurve } from '@ue-too/curve';
320
341
 
321
342
  const curve = new BCurve([
322
- { x: 0, y: 0 },
323
- { x: 50, y: 100 },
324
- { x: 100, y: 0 }
343
+ { x: 0, y: 0 },
344
+ { x: 50, y: 100 },
345
+ { x: 100, y: 0 },
325
346
  ]);
326
347
 
327
348
  const mousePosition = { x: 60, y: 40 };
@@ -333,7 +354,7 @@ console.log('Distance:', distance);
333
354
 
334
355
  // Snap mouse to curve
335
356
  if (distance < 10) {
336
- snapToCurve(point);
357
+ snapToCurve(point);
337
358
  }
338
359
  ```
339
360
 
@@ -343,9 +364,9 @@ if (distance < 10) {
343
364
  import { BCurve } from '@ue-too/curve';
344
365
 
345
366
  const curve = new BCurve([
346
- { x: 0, y: 100 },
347
- { x: 100, y: 0 },
348
- { x: 200, y: 100 }
367
+ { x: 0, y: 100 },
368
+ { x: 100, y: 0 },
369
+ { x: 200, y: 100 },
349
370
  ]);
350
371
 
351
372
  // Create curve offset by 20 pixels
@@ -353,7 +374,7 @@ const offsetCurves = curve.offset(20);
353
374
 
354
375
  // Offset may produce multiple curve segments
355
376
  offsetCurves.forEach(offsetCurve => {
356
- drawCurve(offsetCurve);
377
+ drawCurve(offsetCurve);
357
378
  });
358
379
  ```
359
380
 
@@ -363,9 +384,9 @@ offsetCurves.forEach(offsetCurve => {
363
384
  import { BCurve } from '@ue-too/curve';
364
385
 
365
386
  const curve = new BCurve([
366
- { x: 0, y: 0 },
367
- { x: 50, y: 100 },
368
- { x: 100, y: 0 }
387
+ { x: 0, y: 0 },
388
+ { x: 50, y: 100 },
389
+ { x: 100, y: 0 },
369
390
  ]);
370
391
 
371
392
  // Split at 30% along the curve
@@ -382,23 +403,22 @@ drawCurve(rightPart, 'blue');
382
403
  import { BCurve } from '@ue-too/curve';
383
404
 
384
405
  const curve = new BCurve([
385
- { x: 10, y: 20 },
386
- { x: 150, y: 200 },
387
- { x: 300, y: 50 }
406
+ { x: 10, y: 20 },
407
+ { x: 150, y: 200 },
408
+ { x: 300, y: 50 },
388
409
  ]);
389
410
 
390
411
  const bbox = curve.bbox();
391
412
 
392
413
  // Check if curve is visible in viewport
393
- const isVisible = (
394
- bbox.x.max >= viewport.left &&
395
- bbox.x.min <= viewport.right &&
396
- bbox.y.max >= viewport.top &&
397
- bbox.y.min <= viewport.bottom
398
- );
414
+ const isVisible =
415
+ bbox.x.max >= viewport.left &&
416
+ bbox.x.min <= viewport.right &&
417
+ bbox.y.max >= viewport.top &&
418
+ bbox.y.min <= viewport.bottom;
399
419
 
400
420
  if (isVisible) {
401
- drawCurve(curve);
421
+ drawCurve(curve);
402
422
  }
403
423
  ```
404
424
 
@@ -418,14 +438,14 @@ const point: Point = { x: 10, y: 20 };
418
438
 
419
439
  // Curves are generic over control point count
420
440
  const quadratic: BCurve = new BCurve([
421
- { x: 0, y: 0 },
422
- { x: 50, y: 100 },
423
- { x: 100, y: 0 }
441
+ { x: 0, y: 0 },
442
+ { x: 50, y: 100 },
443
+ { x: 100, y: 0 },
424
444
  ]);
425
445
 
426
446
  // Intersection results are typed
427
447
  const intersections: { selfT: number; otherT: number }[] =
428
- curve1.getCurveIntersections(curve2);
448
+ curve1.getCurveIntersections(curve2);
429
449
  ```
430
450
 
431
451
  ## Design Philosophy
@@ -446,6 +466,7 @@ This library follows these principles:
446
466
  - **Offset curves**: Computationally expensive, may produce multiple segments
447
467
 
448
468
  **Performance Tips:**
469
+
449
470
  - Cache `fullLength` results if curves don't change
450
471
  - Use bounding box checks before expensive intersection tests
451
472
  - Reduce curve complexity with `reduce()` for long curves
package/b-curve.d.ts CHANGED
@@ -1,15 +1,15 @@
1
- import { Line } from "./line";
1
+ import { Line } from './line';
2
2
  type AdvanceAtTWithLengthWithinCurveRes = {
3
- type: "withinCurve";
3
+ type: 'withinCurve';
4
4
  tVal: number;
5
5
  point: Point;
6
6
  };
7
7
  type AdvanceAtWithLengthBeforeCurveRes = {
8
- type: "beforeCurve";
8
+ type: 'beforeCurve';
9
9
  remainLength: number;
10
10
  };
11
11
  type AdvanceAtWithLengthAfterCurveRes = {
12
- type: "afterCurve";
12
+ type: 'afterCurve';
13
13
  remainLength: number;
14
14
  };
15
15
  type AdvanceAtTWithLengthOutofCurveRes = AdvanceAtWithLengthBeforeCurveRes | AdvanceAtWithLengthAfterCurveRes;
@@ -82,6 +82,25 @@ export declare class BCurve {
82
82
  private clearCache;
83
83
  constructor(controlPoints: Point[]);
84
84
  getPointbyPercentage(percentage: number): Point;
85
+ /**
86
+ * Gets the derivative (tangent vector) at a given percentage of the curve length.
87
+ *
88
+ * @param percentage - Percentage of the curve length (0 to 1), where 0 is the start and 1 is the end
89
+ * @returns The derivative vector at the specified percentage along the curve
90
+ *
91
+ * @remarks
92
+ * This method calculates the derivative at a point specified by percentage of arc length,
93
+ * not by the parameter t. This is useful when you want to sample the curve uniformly
94
+ * by distance rather than by parameter value.
95
+ *
96
+ * @example
97
+ * ```typescript
98
+ * const curve = new BCurve([...]);
99
+ * const tangent = curve.derivativeByPercentage(0.5); // Derivative at midpoint
100
+ * const normalized = PointCal.unitVector(tangent); // Normalize for direction
101
+ * ```
102
+ */
103
+ derivativeByPercentage(percentage: number): Point;
85
104
  getDerivativeControlPoints(controlPoints: Point[]): Point[];
86
105
  private validateTVal;
87
106
  getControlPoints(): Point[];
@@ -214,7 +233,13 @@ export declare function offset(curve: BCurve, t: number, d: number): {
214
233
  * Alternative offset implementation using LUT-based approach.
215
234
  * @category Utilities
216
235
  */
217
- export declare function offset2(curve: BCurve, d: number): Point[];
236
+ export declare function offset2(curve: BCurve, d: number): {
237
+ points: Point[];
238
+ aabb: {
239
+ min: Point;
240
+ max: Point;
241
+ };
242
+ };
218
243
  /**
219
244
  * Error thrown when t-value is out of valid range [0, 1].
220
245
  * @category Types
@@ -1,9 +1,9 @@
1
- import { Point } from "@ue-too/math";
1
+ import { Point } from '@ue-too/math';
2
2
  /**
3
3
  * Handle type for Bezier curve control points.
4
4
  * @category Types
5
5
  */
6
- export type HandleType = "ALIGNED" | "VECTOR" | "FREE";
6
+ export type HandleType = 'ALIGNED' | 'VECTOR' | 'FREE';
7
7
  /**
8
8
  * Handle point with position and type.
9
9
  * @category Types
package/index.d.ts CHANGED
@@ -94,7 +94,7 @@
94
94
  * @see {@link BCurve} for the main Bezier curve class
95
95
  * @see {@link Line} for line segment utilities
96
96
  */
97
- export * from "./b-curve";
98
- export * from "./line";
99
- export * from "./composite-curve";
100
- export * from "./path";
97
+ export * from './b-curve';
98
+ export * from './line';
99
+ export * from './composite-curve';
100
+ export * from './path';