@js-draw/math 1.16.0 → 1.17.0
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/cjs/Vec3.d.ts +21 -0
- package/dist/cjs/Vec3.js +28 -0
- package/dist/cjs/lib.d.ts +1 -1
- package/dist/cjs/shapes/Abstract2DShape.d.ts +3 -0
- package/dist/cjs/shapes/BezierJSWrapper.d.ts +15 -5
- package/dist/cjs/shapes/BezierJSWrapper.js +135 -18
- package/dist/cjs/shapes/LineSegment2.d.ts +34 -5
- package/dist/cjs/shapes/LineSegment2.js +63 -10
- package/dist/cjs/shapes/Parameterized2DShape.d.ts +31 -0
- package/dist/cjs/shapes/Parameterized2DShape.js +15 -0
- package/dist/cjs/shapes/Path.d.ts +40 -6
- package/dist/cjs/shapes/Path.js +173 -15
- package/dist/cjs/shapes/PointShape2D.d.ts +14 -3
- package/dist/cjs/shapes/PointShape2D.js +28 -5
- package/dist/cjs/shapes/QuadraticBezier.d.ts +4 -0
- package/dist/cjs/shapes/QuadraticBezier.js +19 -4
- package/dist/cjs/shapes/Rect2.d.ts +3 -0
- package/dist/cjs/shapes/Rect2.js +4 -1
- package/dist/mjs/Vec3.d.ts +21 -0
- package/dist/mjs/Vec3.mjs +28 -0
- package/dist/mjs/lib.d.ts +1 -1
- package/dist/mjs/shapes/Abstract2DShape.d.ts +3 -0
- package/dist/mjs/shapes/BezierJSWrapper.d.ts +15 -5
- package/dist/mjs/shapes/BezierJSWrapper.mjs +133 -18
- package/dist/mjs/shapes/LineSegment2.d.ts +34 -5
- package/dist/mjs/shapes/LineSegment2.mjs +63 -10
- package/dist/mjs/shapes/Parameterized2DShape.d.ts +31 -0
- package/dist/mjs/shapes/Parameterized2DShape.mjs +8 -0
- package/dist/mjs/shapes/Path.d.ts +40 -6
- package/dist/mjs/shapes/Path.mjs +173 -15
- package/dist/mjs/shapes/PointShape2D.d.ts +14 -3
- package/dist/mjs/shapes/PointShape2D.mjs +28 -5
- package/dist/mjs/shapes/QuadraticBezier.d.ts +4 -0
- package/dist/mjs/shapes/QuadraticBezier.mjs +19 -4
- package/dist/mjs/shapes/Rect2.d.ts +3 -0
- package/dist/mjs/shapes/Rect2.mjs +4 -1
- package/package.json +5 -5
- package/src/Vec3.test.ts +26 -7
- package/src/Vec3.ts +30 -0
- package/src/lib.ts +2 -0
- package/src/shapes/Abstract2DShape.ts +3 -0
- package/src/shapes/BezierJSWrapper.ts +154 -14
- package/src/shapes/LineSegment2.test.ts +35 -1
- package/src/shapes/LineSegment2.ts +79 -11
- package/src/shapes/Parameterized2DShape.ts +39 -0
- package/src/shapes/Path.test.ts +63 -3
- package/src/shapes/Path.ts +209 -25
- package/src/shapes/PointShape2D.ts +33 -6
- package/src/shapes/QuadraticBezier.test.ts +48 -12
- package/src/shapes/QuadraticBezier.ts +23 -5
- package/src/shapes/Rect2.ts +4 -1
@@ -2,7 +2,7 @@ import LineSegment2 from './LineSegment2';
|
|
2
2
|
import Mat33 from '../Mat33';
|
3
3
|
import Rect2 from './Rect2';
|
4
4
|
import { Point2 } from '../Vec2';
|
5
|
-
import
|
5
|
+
import Parameterized2DShape from './Parameterized2DShape';
|
6
6
|
export declare enum PathCommandType {
|
7
7
|
LineTo = 0,
|
8
8
|
MoveTo = 1,
|
@@ -29,12 +29,23 @@ export interface MoveToPathCommand {
|
|
29
29
|
point: Point2;
|
30
30
|
}
|
31
31
|
export type PathCommand = CubicBezierPathCommand | QuadraticBezierPathCommand | MoveToPathCommand | LinePathCommand;
|
32
|
-
interface IntersectionResult {
|
33
|
-
curve:
|
34
|
-
|
32
|
+
export interface IntersectionResult {
|
33
|
+
curve: Parameterized2DShape;
|
34
|
+
curveIndex: number;
|
35
|
+
/** Parameter value for the closest point **on** the path to the intersection. @internal @deprecated */
|
35
36
|
parameterValue?: number;
|
37
|
+
/** Point at which the intersection occured. */
|
36
38
|
point: Point2;
|
37
39
|
}
|
40
|
+
/**
|
41
|
+
* Allows indexing a particular part of a path.
|
42
|
+
*
|
43
|
+
* @see {@link Path.at} {@link Path.tangentAt}
|
44
|
+
*/
|
45
|
+
export interface CurveIndexRecord {
|
46
|
+
curveIndex: number;
|
47
|
+
parameterValue: number;
|
48
|
+
}
|
38
49
|
/**
|
39
50
|
* Represents a union of lines and curves.
|
40
51
|
*/
|
@@ -57,7 +68,7 @@ export declare class Path {
|
|
57
68
|
constructor(startPoint: Point2, parts: Readonly<PathCommand>[]);
|
58
69
|
getExactBBox(): Rect2;
|
59
70
|
private cachedGeometry;
|
60
|
-
get geometry():
|
71
|
+
get geometry(): Parameterized2DShape[];
|
61
72
|
/**
|
62
73
|
* Iterates through the start/end points of each component in this path.
|
63
74
|
*
|
@@ -86,10 +97,31 @@ export declare class Path {
|
|
86
97
|
* **Note**: `strokeRadius` is half of a stroke's width.
|
87
98
|
*/
|
88
99
|
intersection(line: LineSegment2, strokeRadius?: number): IntersectionResult[];
|
100
|
+
/**
|
101
|
+
* @returns the nearest point on this path to the given `point`.
|
102
|
+
*
|
103
|
+
* @internal
|
104
|
+
* @beta
|
105
|
+
*/
|
106
|
+
nearestPointTo(point: Point2): IntersectionResult;
|
107
|
+
at(index: CurveIndexRecord): import("../Vec3").Vec3;
|
108
|
+
tangentAt(index: CurveIndexRecord): import("../Vec3").Vec3;
|
89
109
|
private static mapPathCommand;
|
90
110
|
mapPoints(mapping: (point: Point2) => Point2): Path;
|
91
111
|
transformedBy(affineTransfm: Mat33): Path;
|
92
|
-
union(other: Path | null
|
112
|
+
union(other: Path | null, options?: {
|
113
|
+
allowReverse?: boolean;
|
114
|
+
}): Path;
|
115
|
+
/**
|
116
|
+
* @returns a version of this path with the direction reversed.
|
117
|
+
*
|
118
|
+
* Example:
|
119
|
+
* ```ts,runnable,console
|
120
|
+
* import {Path} from '@js-draw/math';
|
121
|
+
* console.log(Path.fromString('m0,0l1,1').reversed()); // -> M1,1 L0,0
|
122
|
+
* ```
|
123
|
+
*/
|
124
|
+
reversed(): Path;
|
93
125
|
private getEndPoint;
|
94
126
|
/**
|
95
127
|
* Like {@link closedRoughlyIntersects} except takes stroke width into account.
|
@@ -103,6 +135,8 @@ export declare class Path {
|
|
103
135
|
*/
|
104
136
|
roughlyIntersects(rect: Rect2, strokeWidth?: number): boolean;
|
105
137
|
closedRoughlyIntersects(rect: Rect2): boolean;
|
138
|
+
/** @returns true if all points on this are equivalent to the points on `other` */
|
139
|
+
eq(other: Path, tolerance?: number): boolean;
|
106
140
|
/**
|
107
141
|
* Returns a path that outlines `rect`.
|
108
142
|
*
|
package/dist/cjs/shapes/Path.js
CHANGED
@@ -236,7 +236,7 @@ class Path {
|
|
236
236
|
for (const { part, distFn, bbox } of uncheckedDistFunctions) {
|
237
237
|
// Skip if impossible for the distance to the target to be lesser than
|
238
238
|
// the current minimum.
|
239
|
-
if (!bbox.grownBy(minDist).containsPoint(point)) {
|
239
|
+
if (isFinite(minDist) && !bbox.grownBy(minDist).containsPoint(point)) {
|
240
240
|
continue;
|
241
241
|
}
|
242
242
|
const currentDist = distFn(point);
|
@@ -274,7 +274,7 @@ class Path {
|
|
274
274
|
});
|
275
275
|
const result = [];
|
276
276
|
const stoppingThreshold = strokeRadius / 1000;
|
277
|
-
// Returns the maximum
|
277
|
+
// Returns the maximum parameter value explored
|
278
278
|
const raymarchFrom = (startPoint,
|
279
279
|
// Direction to march in (multiplies line.direction)
|
280
280
|
directionMultiplier,
|
@@ -318,9 +318,14 @@ class Path {
|
|
318
318
|
if (lastPart && isOnLineSegment && Math.abs(lastDist) < stoppingThreshold) {
|
319
319
|
result.push({
|
320
320
|
point: currentPoint,
|
321
|
-
parameterValue: NaN,
|
321
|
+
parameterValue: NaN, // lastPart.nearestPointTo(currentPoint).parameterValue,
|
322
322
|
curve: lastPart,
|
323
|
+
curveIndex: this.geometry.indexOf(lastPart),
|
323
324
|
});
|
325
|
+
// Slightly increase the parameter value to prevent the same point from being
|
326
|
+
// added to the results twice.
|
327
|
+
const parameterIncrease = strokeRadius / 20 / line.length;
|
328
|
+
lastParameter += isFinite(parameterIncrease) ? parameterIncrease : 0;
|
324
329
|
}
|
325
330
|
return lastParameter;
|
326
331
|
};
|
@@ -353,14 +358,18 @@ class Path {
|
|
353
358
|
if (!line.bbox.intersects(this.bbox.grownBy(strokeRadius ?? 0))) {
|
354
359
|
return [];
|
355
360
|
}
|
361
|
+
let index = 0;
|
356
362
|
for (const part of this.geometry) {
|
357
|
-
const
|
358
|
-
|
363
|
+
const intersections = part.argIntersectsLineSegment(line);
|
364
|
+
for (const intersection of intersections) {
|
359
365
|
result.push({
|
360
366
|
curve: part,
|
361
|
-
|
367
|
+
curveIndex: index,
|
368
|
+
point: part.at(intersection),
|
369
|
+
parameterValue: intersection,
|
362
370
|
});
|
363
371
|
}
|
372
|
+
index++;
|
364
373
|
}
|
365
374
|
// If given a non-zero strokeWidth, attempt to raymarch.
|
366
375
|
// Even if raymarching, we need to collect starting points.
|
@@ -373,6 +382,42 @@ class Path {
|
|
373
382
|
}
|
374
383
|
return result;
|
375
384
|
}
|
385
|
+
/**
|
386
|
+
* @returns the nearest point on this path to the given `point`.
|
387
|
+
*
|
388
|
+
* @internal
|
389
|
+
* @beta
|
390
|
+
*/
|
391
|
+
nearestPointTo(point) {
|
392
|
+
// Find the closest point on this
|
393
|
+
let closestSquareDist = Infinity;
|
394
|
+
let closestPartIndex = 0;
|
395
|
+
let closestParameterValue = 0;
|
396
|
+
let closestPoint = this.startPoint;
|
397
|
+
for (let i = 0; i < this.geometry.length; i++) {
|
398
|
+
const current = this.geometry[i];
|
399
|
+
const nearestPoint = current.nearestPointTo(point);
|
400
|
+
const sqareDist = nearestPoint.point.squareDistanceTo(point);
|
401
|
+
if (i === 0 || sqareDist < closestSquareDist) {
|
402
|
+
closestPartIndex = i;
|
403
|
+
closestSquareDist = sqareDist;
|
404
|
+
closestParameterValue = nearestPoint.parameterValue;
|
405
|
+
closestPoint = nearestPoint.point;
|
406
|
+
}
|
407
|
+
}
|
408
|
+
return {
|
409
|
+
curve: this.geometry[closestPartIndex],
|
410
|
+
curveIndex: closestPartIndex,
|
411
|
+
parameterValue: closestParameterValue,
|
412
|
+
point: closestPoint,
|
413
|
+
};
|
414
|
+
}
|
415
|
+
at(index) {
|
416
|
+
return this.geometry[index.curveIndex].at(index.parameterValue);
|
417
|
+
}
|
418
|
+
tangentAt(index) {
|
419
|
+
return this.geometry[index.curveIndex].tangentAt(index.parameterValue);
|
420
|
+
}
|
376
421
|
static mapPathCommand(part, mapping) {
|
377
422
|
switch (part.kind) {
|
378
423
|
case PathCommandType.MoveTo:
|
@@ -416,18 +461,85 @@ class Path {
|
|
416
461
|
return this.mapPoints(point => affineTransfm.transformVec2(point));
|
417
462
|
}
|
418
463
|
// Creates a new path by joining [other] to the end of this path
|
419
|
-
union(other
|
464
|
+
union(other,
|
465
|
+
// allowReverse: true iff reversing other or this is permitted if it means
|
466
|
+
// no moveTo command is necessary when unioning the paths.
|
467
|
+
options = { allowReverse: true }) {
|
420
468
|
if (!other) {
|
421
469
|
return this;
|
422
470
|
}
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
471
|
+
const thisEnd = this.getEndPoint();
|
472
|
+
let newParts = [];
|
473
|
+
if (thisEnd.eq(other.startPoint)) {
|
474
|
+
newParts = this.parts.concat(other.parts);
|
475
|
+
}
|
476
|
+
else if (options.allowReverse && this.startPoint.eq(other.getEndPoint())) {
|
477
|
+
return other.union(this, { allowReverse: false });
|
478
|
+
}
|
479
|
+
else if (options.allowReverse && this.startPoint.eq(other.startPoint)) {
|
480
|
+
return this.union(other.reversed(), { allowReverse: false });
|
481
|
+
}
|
482
|
+
else {
|
483
|
+
newParts = [
|
484
|
+
...this.parts,
|
485
|
+
{
|
486
|
+
kind: PathCommandType.MoveTo,
|
487
|
+
point: other.startPoint,
|
488
|
+
},
|
489
|
+
...other.parts,
|
490
|
+
];
|
491
|
+
}
|
492
|
+
return new Path(this.startPoint, newParts);
|
493
|
+
}
|
494
|
+
/**
|
495
|
+
* @returns a version of this path with the direction reversed.
|
496
|
+
*
|
497
|
+
* Example:
|
498
|
+
* ```ts,runnable,console
|
499
|
+
* import {Path} from '@js-draw/math';
|
500
|
+
* console.log(Path.fromString('m0,0l1,1').reversed()); // -> M1,1 L0,0
|
501
|
+
* ```
|
502
|
+
*/
|
503
|
+
reversed() {
|
504
|
+
const newStart = this.getEndPoint();
|
505
|
+
const newParts = [];
|
506
|
+
let lastPoint = this.startPoint;
|
507
|
+
for (const part of this.parts) {
|
508
|
+
switch (part.kind) {
|
509
|
+
case PathCommandType.LineTo:
|
510
|
+
case PathCommandType.MoveTo:
|
511
|
+
newParts.push({
|
512
|
+
kind: part.kind,
|
513
|
+
point: lastPoint,
|
514
|
+
});
|
515
|
+
lastPoint = part.point;
|
516
|
+
break;
|
517
|
+
case PathCommandType.CubicBezierTo:
|
518
|
+
newParts.push({
|
519
|
+
kind: part.kind,
|
520
|
+
controlPoint1: part.controlPoint2,
|
521
|
+
controlPoint2: part.controlPoint1,
|
522
|
+
endPoint: lastPoint,
|
523
|
+
});
|
524
|
+
lastPoint = part.endPoint;
|
525
|
+
break;
|
526
|
+
case PathCommandType.QuadraticBezierTo:
|
527
|
+
newParts.push({
|
528
|
+
kind: part.kind,
|
529
|
+
controlPoint: part.controlPoint,
|
530
|
+
endPoint: lastPoint,
|
531
|
+
});
|
532
|
+
lastPoint = part.endPoint;
|
533
|
+
break;
|
534
|
+
default:
|
535
|
+
{
|
536
|
+
const exhaustivenessCheck = part;
|
537
|
+
return exhaustivenessCheck;
|
538
|
+
}
|
539
|
+
}
|
540
|
+
}
|
541
|
+
newParts.reverse();
|
542
|
+
return new Path(newStart, newParts);
|
431
543
|
}
|
432
544
|
getEndPoint() {
|
433
545
|
if (this.parts.length === 0) {
|
@@ -519,6 +631,52 @@ class Path {
|
|
519
631
|
// Even? Probably no intersection.
|
520
632
|
return false;
|
521
633
|
}
|
634
|
+
/** @returns true if all points on this are equivalent to the points on `other` */
|
635
|
+
eq(other, tolerance) {
|
636
|
+
if (other.parts.length !== this.parts.length) {
|
637
|
+
return false;
|
638
|
+
}
|
639
|
+
for (let i = 0; i < this.parts.length; i++) {
|
640
|
+
const part1 = this.parts[i];
|
641
|
+
const part2 = other.parts[i];
|
642
|
+
switch (part1.kind) {
|
643
|
+
case PathCommandType.LineTo:
|
644
|
+
case PathCommandType.MoveTo:
|
645
|
+
if (part1.kind !== part2.kind) {
|
646
|
+
return false;
|
647
|
+
}
|
648
|
+
else if (!part1.point.eq(part2.point, tolerance)) {
|
649
|
+
return false;
|
650
|
+
}
|
651
|
+
break;
|
652
|
+
case PathCommandType.CubicBezierTo:
|
653
|
+
if (part1.kind !== part2.kind) {
|
654
|
+
return false;
|
655
|
+
}
|
656
|
+
else if (!part1.controlPoint1.eq(part2.controlPoint1, tolerance)
|
657
|
+
|| !part1.controlPoint2.eq(part2.controlPoint2, tolerance)
|
658
|
+
|| !part1.endPoint.eq(part2.endPoint, tolerance)) {
|
659
|
+
return false;
|
660
|
+
}
|
661
|
+
break;
|
662
|
+
case PathCommandType.QuadraticBezierTo:
|
663
|
+
if (part1.kind !== part2.kind) {
|
664
|
+
return false;
|
665
|
+
}
|
666
|
+
else if (!part1.controlPoint.eq(part2.controlPoint, tolerance)
|
667
|
+
|| !part1.endPoint.eq(part2.endPoint, tolerance)) {
|
668
|
+
return false;
|
669
|
+
}
|
670
|
+
break;
|
671
|
+
default:
|
672
|
+
{
|
673
|
+
const exhaustivenessCheck = part1;
|
674
|
+
return exhaustivenessCheck;
|
675
|
+
}
|
676
|
+
}
|
677
|
+
}
|
678
|
+
return true;
|
679
|
+
}
|
522
680
|
/**
|
523
681
|
* Returns a path that outlines `rect`.
|
524
682
|
*
|
@@ -1,18 +1,29 @@
|
|
1
1
|
import { Point2 } from '../Vec2';
|
2
2
|
import Vec3 from '../Vec3';
|
3
|
-
import Abstract2DShape from './Abstract2DShape';
|
4
3
|
import LineSegment2 from './LineSegment2';
|
4
|
+
import Parameterized2DShape from './Parameterized2DShape';
|
5
5
|
import Rect2 from './Rect2';
|
6
6
|
/**
|
7
7
|
* Like a {@link Point2}, but with additional functionality (e.g. SDF).
|
8
8
|
*
|
9
9
|
* Access the internal `Point2` using the `p` property.
|
10
10
|
*/
|
11
|
-
declare class PointShape2D extends
|
11
|
+
declare class PointShape2D extends Parameterized2DShape {
|
12
12
|
readonly p: Point2;
|
13
13
|
constructor(p: Point2);
|
14
14
|
signedDistance(point: Vec3): number;
|
15
|
-
|
15
|
+
argIntersectsLineSegment(lineSegment: LineSegment2, epsilon?: number): number[];
|
16
16
|
getTightBoundingBox(): Rect2;
|
17
|
+
at(_t: number): Vec3;
|
18
|
+
/**
|
19
|
+
* Returns an arbitrary unit-length vector.
|
20
|
+
*/
|
21
|
+
normalAt(_t: number): Vec3;
|
22
|
+
tangentAt(_t: number): Vec3;
|
23
|
+
splitAt(_t: number): [PointShape2D];
|
24
|
+
nearestPointTo(_point: Point2): {
|
25
|
+
point: Vec3;
|
26
|
+
parameterValue: number;
|
27
|
+
};
|
17
28
|
}
|
18
29
|
export default PointShape2D;
|
@@ -3,29 +3,52 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
4
|
};
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
-
const
|
6
|
+
const Vec2_1 = require("../Vec2");
|
7
|
+
const Parameterized2DShape_1 = __importDefault(require("./Parameterized2DShape"));
|
7
8
|
const Rect2_1 = __importDefault(require("./Rect2"));
|
8
9
|
/**
|
9
10
|
* Like a {@link Point2}, but with additional functionality (e.g. SDF).
|
10
11
|
*
|
11
12
|
* Access the internal `Point2` using the `p` property.
|
12
13
|
*/
|
13
|
-
class PointShape2D extends
|
14
|
+
class PointShape2D extends Parameterized2DShape_1.default {
|
14
15
|
constructor(p) {
|
15
16
|
super();
|
16
17
|
this.p = p;
|
17
18
|
}
|
18
19
|
signedDistance(point) {
|
19
|
-
return this.p.
|
20
|
+
return this.p.distanceTo(point);
|
20
21
|
}
|
21
|
-
|
22
|
+
argIntersectsLineSegment(lineSegment, epsilon) {
|
22
23
|
if (lineSegment.containsPoint(this.p, epsilon)) {
|
23
|
-
return [
|
24
|
+
return [0];
|
24
25
|
}
|
25
26
|
return [];
|
26
27
|
}
|
27
28
|
getTightBoundingBox() {
|
28
29
|
return new Rect2_1.default(this.p.x, this.p.y, 0, 0);
|
29
30
|
}
|
31
|
+
at(_t) {
|
32
|
+
return this.p;
|
33
|
+
}
|
34
|
+
/**
|
35
|
+
* Returns an arbitrary unit-length vector.
|
36
|
+
*/
|
37
|
+
normalAt(_t) {
|
38
|
+
// Return a vector that makes sense.
|
39
|
+
return Vec2_1.Vec2.unitY;
|
40
|
+
}
|
41
|
+
tangentAt(_t) {
|
42
|
+
return Vec2_1.Vec2.unitX;
|
43
|
+
}
|
44
|
+
splitAt(_t) {
|
45
|
+
return [this];
|
46
|
+
}
|
47
|
+
nearestPointTo(_point) {
|
48
|
+
return {
|
49
|
+
point: this.p,
|
50
|
+
parameterValue: 0,
|
51
|
+
};
|
52
|
+
}
|
30
53
|
}
|
31
54
|
exports.default = PointShape2D;
|
@@ -18,11 +18,15 @@ export declare class QuadraticBezier extends BezierJSWrapper {
|
|
18
18
|
*/
|
19
19
|
private static componentAt;
|
20
20
|
private static derivativeComponentAt;
|
21
|
+
private static secondDerivativeComponentAt;
|
21
22
|
/**
|
22
23
|
* @returns the curve evaluated at `t`.
|
24
|
+
*
|
25
|
+
* `t` should be a number in `[0, 1]`.
|
23
26
|
*/
|
24
27
|
at(t: number): Point2;
|
25
28
|
derivativeAt(t: number): Point2;
|
29
|
+
secondDerivativeAt(t: number): Point2;
|
26
30
|
normal(t: number): Vec2;
|
27
31
|
/** @returns an overestimate of this shape's bounding box. */
|
28
32
|
getLooseBoundingBox(): Rect2;
|
@@ -31,10 +31,19 @@ class QuadraticBezier extends BezierJSWrapper_1.default {
|
|
31
31
|
static derivativeComponentAt(t, p0, p1, p2) {
|
32
32
|
return -2 * p0 + 2 * p1 + 2 * t * (p0 - 2 * p1 + p2);
|
33
33
|
}
|
34
|
+
static secondDerivativeComponentAt(t, p0, p1, p2) {
|
35
|
+
return 2 * (p0 - 2 * p1 + p2);
|
36
|
+
}
|
34
37
|
/**
|
35
38
|
* @returns the curve evaluated at `t`.
|
39
|
+
*
|
40
|
+
* `t` should be a number in `[0, 1]`.
|
36
41
|
*/
|
37
42
|
at(t) {
|
43
|
+
if (t === 0)
|
44
|
+
return this.p0;
|
45
|
+
if (t === 1)
|
46
|
+
return this.p2;
|
38
47
|
const p0 = this.p0;
|
39
48
|
const p1 = this.p1;
|
40
49
|
const p2 = this.p2;
|
@@ -46,6 +55,12 @@ class QuadraticBezier extends BezierJSWrapper_1.default {
|
|
46
55
|
const p2 = this.p2;
|
47
56
|
return Vec2_1.Vec2.of(QuadraticBezier.derivativeComponentAt(t, p0.x, p1.x, p2.x), QuadraticBezier.derivativeComponentAt(t, p0.y, p1.y, p2.y));
|
48
57
|
}
|
58
|
+
secondDerivativeAt(t) {
|
59
|
+
const p0 = this.p0;
|
60
|
+
const p1 = this.p1;
|
61
|
+
const p2 = this.p2;
|
62
|
+
return Vec2_1.Vec2.of(QuadraticBezier.secondDerivativeComponentAt(t, p0.x, p1.x, p2.x), QuadraticBezier.secondDerivativeComponentAt(t, p0.y, p1.y, p2.y));
|
63
|
+
}
|
49
64
|
normal(t) {
|
50
65
|
const tangent = this.derivativeAt(t);
|
51
66
|
return tangent.orthog().normalized();
|
@@ -106,10 +121,10 @@ class QuadraticBezier extends BezierJSWrapper_1.default {
|
|
106
121
|
}
|
107
122
|
const at1 = this.at(min1);
|
108
123
|
const at2 = this.at(min2);
|
109
|
-
const sqrDist1 = at1.
|
110
|
-
const sqrDist2 = at2.
|
111
|
-
const sqrDist3 = this.at(0).
|
112
|
-
const sqrDist4 = this.at(1).
|
124
|
+
const sqrDist1 = at1.squareDistanceTo(point);
|
125
|
+
const sqrDist2 = at2.squareDistanceTo(point);
|
126
|
+
const sqrDist3 = this.at(0).squareDistanceTo(point);
|
127
|
+
const sqrDist4 = this.at(1).squareDistanceTo(point);
|
113
128
|
return Math.sqrt(Math.min(sqrDist1, sqrDist2, sqrDist3, sqrDist4));
|
114
129
|
}
|
115
130
|
getPoints() {
|
@@ -25,6 +25,9 @@ export declare class Rect2 extends Abstract2DShape {
|
|
25
25
|
resizedTo(size: Vec2): Rect2;
|
26
26
|
containsPoint(other: Point2): boolean;
|
27
27
|
containsRect(other: Rect2): boolean;
|
28
|
+
/**
|
29
|
+
* @returns true iff this and `other` overlap
|
30
|
+
*/
|
28
31
|
intersects(other: Rect2): boolean;
|
29
32
|
intersection(other: Rect2): Rect2 | null;
|
30
33
|
union(other: Rect2): Rect2;
|
package/dist/cjs/shapes/Rect2.js
CHANGED
@@ -44,6 +44,9 @@ class Rect2 extends Abstract2DShape_1.default {
|
|
44
44
|
&& this.x + this.w >= other.x + other.w
|
45
45
|
&& this.y + this.h >= other.y + other.h;
|
46
46
|
}
|
47
|
+
/**
|
48
|
+
* @returns true iff this and `other` overlap
|
49
|
+
*/
|
47
50
|
intersects(other) {
|
48
51
|
// Project along x/y axes.
|
49
52
|
const thisMinX = this.x;
|
@@ -130,7 +133,7 @@ class Rect2 extends Abstract2DShape_1.default {
|
|
130
133
|
let closest = null;
|
131
134
|
let closestDist = null;
|
132
135
|
for (const point of closestEdgePoints) {
|
133
|
-
const dist = point.
|
136
|
+
const dist = point.distanceTo(target);
|
134
137
|
if (closestDist === null || dist < closestDist) {
|
135
138
|
closest = point;
|
136
139
|
closestDist = dist;
|
package/dist/mjs/Vec3.d.ts
CHANGED
@@ -35,11 +35,31 @@ export declare class Vec3 {
|
|
35
35
|
length(): number;
|
36
36
|
magnitude(): number;
|
37
37
|
magnitudeSquared(): number;
|
38
|
+
/**
|
39
|
+
* Interpreting this vector as a point in ℝ^3, computes the square distance
|
40
|
+
* to another point, `p`.
|
41
|
+
*
|
42
|
+
* Equivalent to `.minus(p).magnitudeSquared()`.
|
43
|
+
*/
|
44
|
+
squareDistanceTo(p: Vec3): number;
|
45
|
+
/**
|
46
|
+
* Interpreting this vector as a point in ℝ³, returns the distance to the point
|
47
|
+
* `p`.
|
48
|
+
*
|
49
|
+
* Equivalent to `.minus(p).magnitude()`.
|
50
|
+
*/
|
51
|
+
distanceTo(p: Vec3): number;
|
38
52
|
/**
|
39
53
|
* Returns the entry of this with the greatest magnitude.
|
40
54
|
*
|
41
55
|
* In other words, returns $\max \{ |x| : x \in {\bf v} \}$, where ${\bf v}$ is the set of
|
42
56
|
* all entries of this vector.
|
57
|
+
*
|
58
|
+
* **Example**:
|
59
|
+
* ```ts,runnable,console
|
60
|
+
* import { Vec3 } from '@js-draw/math';
|
61
|
+
* console.log(Vec3.of(-1, -10, 8).maximumEntryMagnitude()); // -> 10
|
62
|
+
* ```
|
43
63
|
*/
|
44
64
|
maximumEntryMagnitude(): number;
|
45
65
|
/**
|
@@ -50,6 +70,7 @@ export declare class Vec3 {
|
|
50
70
|
* As such, observing that `Math.atan2(-0, -1)` $\approx -\pi$ and `Math.atan2(0, -1)`$\approx \pi$
|
51
71
|
* the resultant angle is in the range $[-\pi, pi]$.
|
52
72
|
*
|
73
|
+
* **Example**:
|
53
74
|
* ```ts,runnable,console
|
54
75
|
* import { Vec2 } from '@js-draw/math';
|
55
76
|
* console.log(Vec2.of(-1, -0).angle()); // atan2(-0, -1)
|
package/dist/mjs/Vec3.mjs
CHANGED
@@ -55,11 +55,38 @@ export class Vec3 {
|
|
55
55
|
magnitudeSquared() {
|
56
56
|
return this.dot(this);
|
57
57
|
}
|
58
|
+
/**
|
59
|
+
* Interpreting this vector as a point in ℝ^3, computes the square distance
|
60
|
+
* to another point, `p`.
|
61
|
+
*
|
62
|
+
* Equivalent to `.minus(p).magnitudeSquared()`.
|
63
|
+
*/
|
64
|
+
squareDistanceTo(p) {
|
65
|
+
const dx = this.x - p.x;
|
66
|
+
const dy = this.y - p.y;
|
67
|
+
const dz = this.z - p.z;
|
68
|
+
return dx * dx + dy * dy + dz * dz;
|
69
|
+
}
|
70
|
+
/**
|
71
|
+
* Interpreting this vector as a point in ℝ³, returns the distance to the point
|
72
|
+
* `p`.
|
73
|
+
*
|
74
|
+
* Equivalent to `.minus(p).magnitude()`.
|
75
|
+
*/
|
76
|
+
distanceTo(p) {
|
77
|
+
return Math.sqrt(this.squareDistanceTo(p));
|
78
|
+
}
|
58
79
|
/**
|
59
80
|
* Returns the entry of this with the greatest magnitude.
|
60
81
|
*
|
61
82
|
* In other words, returns $\max \{ |x| : x \in {\bf v} \}$, where ${\bf v}$ is the set of
|
62
83
|
* all entries of this vector.
|
84
|
+
*
|
85
|
+
* **Example**:
|
86
|
+
* ```ts,runnable,console
|
87
|
+
* import { Vec3 } from '@js-draw/math';
|
88
|
+
* console.log(Vec3.of(-1, -10, 8).maximumEntryMagnitude()); // -> 10
|
89
|
+
* ```
|
63
90
|
*/
|
64
91
|
maximumEntryMagnitude() {
|
65
92
|
return Math.max(Math.abs(this.x), Math.max(Math.abs(this.y), Math.abs(this.z)));
|
@@ -72,6 +99,7 @@ export class Vec3 {
|
|
72
99
|
* As such, observing that `Math.atan2(-0, -1)` $\approx -\pi$ and `Math.atan2(0, -1)`$\approx \pi$
|
73
100
|
* the resultant angle is in the range $[-\pi, pi]$.
|
74
101
|
*
|
102
|
+
* **Example**:
|
75
103
|
* ```ts,runnable,console
|
76
104
|
* import { Vec2 } from '@js-draw/math';
|
77
105
|
* console.log(Vec2.of(-1, -0).angle()); // atan2(-0, -1)
|
package/dist/mjs/lib.d.ts
CHANGED
@@ -17,7 +17,7 @@
|
|
17
17
|
* @packageDocumentation
|
18
18
|
*/
|
19
19
|
export { LineSegment2 } from './shapes/LineSegment2';
|
20
|
-
export { Path, PathCommandType, PathCommand, LinePathCommand, MoveToPathCommand, QuadraticBezierPathCommand, CubicBezierPathCommand, } from './shapes/Path';
|
20
|
+
export { Path, IntersectionResult as PathIntersectionResult, CurveIndexRecord as PathCurveIndex, PathCommandType, PathCommand, LinePathCommand, MoveToPathCommand, QuadraticBezierPathCommand, CubicBezierPathCommand, } from './shapes/Path';
|
21
21
|
export { Rect2 } from './shapes/Rect2';
|
22
22
|
export { QuadraticBezier } from './shapes/QuadraticBezier';
|
23
23
|
export { Abstract2DShape } from './shapes/Abstract2DShape';
|
@@ -38,6 +38,9 @@ export declare abstract class Abstract2DShape {
|
|
38
38
|
containsPoint(point: Point2, epsilon?: number): boolean;
|
39
39
|
/**
|
40
40
|
* Returns a bounding box that precisely fits the content of this shape.
|
41
|
+
*
|
42
|
+
* **Note**: This bounding box should aligned with the x/y axes. (Thus, it may be
|
43
|
+
* possible to find a tighter bounding box not axes-aligned).
|
41
44
|
*/
|
42
45
|
abstract getTightBoundingBox(): Rect2;
|
43
46
|
/**
|
@@ -1,21 +1,22 @@
|
|
1
1
|
import { Bezier } from 'bezier-js';
|
2
2
|
import { Point2, Vec2 } from '../Vec2';
|
3
|
-
import Abstract2DShape from './Abstract2DShape';
|
4
3
|
import LineSegment2 from './LineSegment2';
|
5
4
|
import Rect2 from './Rect2';
|
5
|
+
import Parameterized2DShape from './Parameterized2DShape';
|
6
6
|
/**
|
7
7
|
* A lazy-initializing wrapper around Bezier-js.
|
8
8
|
*
|
9
9
|
* Subclasses may override `at`, `derivativeAt`, and `normal` with functions
|
10
10
|
* that do not initialize a `bezier-js` `Bezier`.
|
11
11
|
*
|
12
|
-
* Do not use this class directly
|
12
|
+
* **Do not use this class directly.** It may be removed/replaced in a future release.
|
13
13
|
* @internal
|
14
14
|
*/
|
15
|
-
declare abstract class BezierJSWrapper extends
|
15
|
+
export declare abstract class BezierJSWrapper extends Parameterized2DShape {
|
16
16
|
#private;
|
17
|
+
protected constructor(bezierJsBezier?: Bezier);
|
17
18
|
/** Returns the start, control points, and end point of this Bézier. */
|
18
|
-
abstract getPoints(): Point2[];
|
19
|
+
abstract getPoints(): readonly Point2[];
|
19
20
|
protected getBezier(): Bezier;
|
20
21
|
signedDistance(point: Point2): number;
|
21
22
|
/**
|
@@ -29,8 +30,17 @@ declare abstract class BezierJSWrapper extends Abstract2DShape {
|
|
29
30
|
*/
|
30
31
|
at(t: number): Point2;
|
31
32
|
derivativeAt(t: number): Point2;
|
33
|
+
secondDerivativeAt(t: number): Point2;
|
32
34
|
normal(t: number): Vec2;
|
35
|
+
normalAt(t: number): Vec2;
|
36
|
+
tangentAt(t: number): Vec2;
|
33
37
|
getTightBoundingBox(): Rect2;
|
34
|
-
|
38
|
+
argIntersectsLineSegment(line: LineSegment2): number[];
|
39
|
+
splitAt(t: number): [BezierJSWrapper] | [BezierJSWrapper, BezierJSWrapper];
|
40
|
+
nearestPointTo(point: Point2): {
|
41
|
+
parameterValue: number;
|
42
|
+
point: import("../Vec3").Vec3;
|
43
|
+
};
|
44
|
+
toString(): string;
|
35
45
|
}
|
36
46
|
export default BezierJSWrapper;
|