@js-draw/math 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +3 -0
- package/build-config.json +4 -0
- package/dist/cjs/Color4.d.ts +83 -0
- package/dist/cjs/Color4.js +277 -0
- package/dist/cjs/Mat33.d.ts +131 -0
- package/dist/cjs/Mat33.js +345 -0
- package/dist/cjs/Vec2.d.ts +42 -0
- package/dist/cjs/Vec2.js +48 -0
- package/dist/cjs/Vec3.d.ts +126 -0
- package/dist/cjs/Vec3.js +203 -0
- package/dist/cjs/lib.d.ts +27 -0
- package/dist/cjs/lib.js +42 -0
- package/dist/cjs/polynomial/solveQuadratic.d.ts +9 -0
- package/dist/cjs/polynomial/solveQuadratic.js +39 -0
- package/dist/cjs/rounding.d.ts +15 -0
- package/dist/cjs/rounding.js +146 -0
- package/dist/cjs/shapes/Abstract2DShape.d.ts +49 -0
- package/dist/cjs/shapes/Abstract2DShape.js +38 -0
- package/dist/cjs/shapes/BezierJSWrapper.d.ts +36 -0
- package/dist/cjs/shapes/BezierJSWrapper.js +94 -0
- package/dist/cjs/shapes/CubicBezier.d.ts +17 -0
- package/dist/cjs/shapes/CubicBezier.js +35 -0
- package/dist/cjs/shapes/LineSegment2.d.ts +70 -0
- package/dist/cjs/shapes/LineSegment2.js +183 -0
- package/dist/cjs/shapes/Path.d.ts +96 -0
- package/dist/cjs/shapes/Path.js +766 -0
- package/dist/cjs/shapes/PointShape2D.d.ts +18 -0
- package/dist/cjs/shapes/PointShape2D.js +31 -0
- package/dist/cjs/shapes/QuadraticBezier.d.ts +35 -0
- package/dist/cjs/shapes/QuadraticBezier.js +120 -0
- package/dist/cjs/shapes/Rect2.d.ts +58 -0
- package/dist/cjs/shapes/Rect2.js +259 -0
- package/dist/cjs/shapes/Triangle.d.ts +46 -0
- package/dist/cjs/shapes/Triangle.js +126 -0
- package/dist/mjs/Color4.d.ts +83 -0
- package/dist/mjs/Color4.mjs +271 -0
- package/dist/mjs/Mat33.d.ts +131 -0
- package/dist/mjs/Mat33.mjs +338 -0
- package/dist/mjs/Vec2.d.ts +42 -0
- package/dist/mjs/Vec2.mjs +42 -0
- package/dist/mjs/Vec3.d.ts +126 -0
- package/dist/mjs/Vec3.mjs +199 -0
- package/dist/mjs/lib.d.ts +27 -0
- package/dist/mjs/lib.mjs +29 -0
- package/dist/mjs/polynomial/solveQuadratic.d.ts +9 -0
- package/dist/mjs/polynomial/solveQuadratic.mjs +37 -0
- package/dist/mjs/rounding.d.ts +15 -0
- package/dist/mjs/rounding.mjs +139 -0
- package/dist/mjs/shapes/Abstract2DShape.d.ts +49 -0
- package/dist/mjs/shapes/Abstract2DShape.mjs +36 -0
- package/dist/mjs/shapes/BezierJSWrapper.d.ts +36 -0
- package/dist/mjs/shapes/BezierJSWrapper.mjs +89 -0
- package/dist/mjs/shapes/CubicBezier.d.ts +17 -0
- package/dist/mjs/shapes/CubicBezier.mjs +30 -0
- package/dist/mjs/shapes/LineSegment2.d.ts +70 -0
- package/dist/mjs/shapes/LineSegment2.mjs +176 -0
- package/dist/mjs/shapes/Path.d.ts +96 -0
- package/dist/mjs/shapes/Path.mjs +759 -0
- package/dist/mjs/shapes/PointShape2D.d.ts +18 -0
- package/dist/mjs/shapes/PointShape2D.mjs +26 -0
- package/dist/mjs/shapes/QuadraticBezier.d.ts +35 -0
- package/dist/mjs/shapes/QuadraticBezier.mjs +113 -0
- package/dist/mjs/shapes/Rect2.d.ts +58 -0
- package/dist/mjs/shapes/Rect2.mjs +252 -0
- package/dist/mjs/shapes/Triangle.d.ts +46 -0
- package/dist/mjs/shapes/Triangle.mjs +121 -0
- package/package.json +48 -0
- package/tsconfig.json +7 -0
- package/typedoc.json +5 -0
@@ -0,0 +1,94 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
5
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
6
|
+
};
|
7
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
8
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
9
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
10
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
11
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
12
|
+
};
|
13
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
14
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
15
|
+
};
|
16
|
+
var _BezierJSWrapper_bezierJs;
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
18
|
+
const bezier_js_1 = require("bezier-js");
|
19
|
+
const Vec2_1 = require("../Vec2");
|
20
|
+
const Abstract2DShape_1 = __importDefault(require("./Abstract2DShape"));
|
21
|
+
const Rect2_1 = __importDefault(require("./Rect2"));
|
22
|
+
/**
|
23
|
+
* A lazy-initializing wrapper around Bezier-js.
|
24
|
+
*
|
25
|
+
* Subclasses may override `at`, `derivativeAt`, and `normal` with functions
|
26
|
+
* that do not initialize a `bezier-js` `Bezier`.
|
27
|
+
*
|
28
|
+
* Do not use this class directly. It may be removed/replaced in a future release.
|
29
|
+
* @internal
|
30
|
+
*/
|
31
|
+
class BezierJSWrapper extends Abstract2DShape_1.default {
|
32
|
+
constructor() {
|
33
|
+
super(...arguments);
|
34
|
+
_BezierJSWrapper_bezierJs.set(this, null);
|
35
|
+
}
|
36
|
+
getBezier() {
|
37
|
+
if (!__classPrivateFieldGet(this, _BezierJSWrapper_bezierJs, "f")) {
|
38
|
+
__classPrivateFieldSet(this, _BezierJSWrapper_bezierJs, new bezier_js_1.Bezier(this.getPoints().map(p => p.xy)), "f");
|
39
|
+
}
|
40
|
+
return __classPrivateFieldGet(this, _BezierJSWrapper_bezierJs, "f");
|
41
|
+
}
|
42
|
+
signedDistance(point) {
|
43
|
+
// .d: Distance
|
44
|
+
return this.getBezier().project(point.xy).d;
|
45
|
+
}
|
46
|
+
/**
|
47
|
+
* @returns the (more) exact distance from `point` to this.
|
48
|
+
*
|
49
|
+
* @see {@link approximateDistance}
|
50
|
+
*/
|
51
|
+
distance(point) {
|
52
|
+
// A Bézier curve has no interior, thus, signed distance is the same as distance.
|
53
|
+
return this.signedDistance(point);
|
54
|
+
}
|
55
|
+
/**
|
56
|
+
* @returns the curve evaluated at `t`.
|
57
|
+
*/
|
58
|
+
at(t) {
|
59
|
+
return Vec2_1.Vec2.ofXY(this.getBezier().get(t));
|
60
|
+
}
|
61
|
+
derivativeAt(t) {
|
62
|
+
return Vec2_1.Vec2.ofXY(this.getBezier().derivative(t));
|
63
|
+
}
|
64
|
+
normal(t) {
|
65
|
+
return Vec2_1.Vec2.ofXY(this.getBezier().normal(t));
|
66
|
+
}
|
67
|
+
getTightBoundingBox() {
|
68
|
+
const bbox = this.getBezier().bbox();
|
69
|
+
const width = bbox.x.max - bbox.x.min;
|
70
|
+
const height = bbox.y.max - bbox.y.min;
|
71
|
+
return new Rect2_1.default(bbox.x.min, bbox.y.min, width, height);
|
72
|
+
}
|
73
|
+
intersectsLineSegment(line) {
|
74
|
+
const bezier = this.getBezier();
|
75
|
+
const intersectionPoints = bezier.intersects(line).map(t => {
|
76
|
+
// We're using the .intersects(line) function, which is documented
|
77
|
+
// to always return numbers. However, to satisfy the type checker (and
|
78
|
+
// possibly improperly-defined types),
|
79
|
+
if (typeof t === 'string') {
|
80
|
+
t = parseFloat(t);
|
81
|
+
}
|
82
|
+
const point = Vec2_1.Vec2.ofXY(bezier.get(t));
|
83
|
+
// Ensure that the intersection is on the line segment
|
84
|
+
if (point.minus(line.p1).magnitude() > line.length
|
85
|
+
|| point.minus(line.p2).magnitude() > line.length) {
|
86
|
+
return null;
|
87
|
+
}
|
88
|
+
return point;
|
89
|
+
}).filter(entry => entry !== null);
|
90
|
+
return intersectionPoints;
|
91
|
+
}
|
92
|
+
}
|
93
|
+
_BezierJSWrapper_bezierJs = new WeakMap();
|
94
|
+
exports.default = BezierJSWrapper;
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import { Point2 } from '../Vec2';
|
2
|
+
import BezierJSWrapper from './BezierJSWrapper';
|
3
|
+
import Rect2 from './Rect2';
|
4
|
+
/**
|
5
|
+
* A wrapper around [`bezier-js`](https://github.com/Pomax/bezierjs)'s cubic Bezier.
|
6
|
+
*/
|
7
|
+
declare class CubicBezier extends BezierJSWrapper {
|
8
|
+
readonly p0: Point2;
|
9
|
+
readonly p1: Point2;
|
10
|
+
readonly p2: Point2;
|
11
|
+
readonly p3: Point2;
|
12
|
+
constructor(p0: Point2, p1: Point2, p2: Point2, p3: Point2);
|
13
|
+
getPoints(): import("../Vec3").Vec3[];
|
14
|
+
/** Returns an overestimate of this shape's bounding box. */
|
15
|
+
getLooseBoundingBox(): Rect2;
|
16
|
+
}
|
17
|
+
export default CubicBezier;
|
@@ -0,0 +1,35 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
const BezierJSWrapper_1 = __importDefault(require("./BezierJSWrapper"));
|
7
|
+
const Rect2_1 = __importDefault(require("./Rect2"));
|
8
|
+
/**
|
9
|
+
* A wrapper around [`bezier-js`](https://github.com/Pomax/bezierjs)'s cubic Bezier.
|
10
|
+
*/
|
11
|
+
class CubicBezier extends BezierJSWrapper_1.default {
|
12
|
+
constructor(
|
13
|
+
// Start point
|
14
|
+
p0,
|
15
|
+
// Control point 1
|
16
|
+
p1,
|
17
|
+
// Control point 2
|
18
|
+
p2,
|
19
|
+
// End point
|
20
|
+
p3) {
|
21
|
+
super();
|
22
|
+
this.p0 = p0;
|
23
|
+
this.p1 = p1;
|
24
|
+
this.p2 = p2;
|
25
|
+
this.p3 = p3;
|
26
|
+
}
|
27
|
+
getPoints() {
|
28
|
+
return [this.p0, this.p1, this.p2, this.p3];
|
29
|
+
}
|
30
|
+
/** Returns an overestimate of this shape's bounding box. */
|
31
|
+
getLooseBoundingBox() {
|
32
|
+
return Rect2_1.default.bboxOf([this.p0, this.p1, this.p2, this.p3]);
|
33
|
+
}
|
34
|
+
}
|
35
|
+
exports.default = CubicBezier;
|
@@ -0,0 +1,70 @@
|
|
1
|
+
import Mat33 from '../Mat33';
|
2
|
+
import Rect2 from './Rect2';
|
3
|
+
import { Vec2, Point2 } from '../Vec2';
|
4
|
+
import Abstract2DShape from './Abstract2DShape';
|
5
|
+
interface IntersectionResult {
|
6
|
+
point: Point2;
|
7
|
+
t: number;
|
8
|
+
}
|
9
|
+
/** Represents a line segment. A `LineSegment2` is immutable. */
|
10
|
+
export declare class LineSegment2 extends Abstract2DShape {
|
11
|
+
private readonly point1;
|
12
|
+
private readonly point2;
|
13
|
+
/**
|
14
|
+
* The **unit** direction vector of this line segment, from
|
15
|
+
* `point1` to `point2`.
|
16
|
+
*
|
17
|
+
* In other words, `direction` is `point2.minus(point1).normalized()`
|
18
|
+
* (perhaps except when `point1` is equal to `point2`).
|
19
|
+
*/
|
20
|
+
readonly direction: Vec2;
|
21
|
+
/** The distance between `point1` and `point2`. */
|
22
|
+
readonly length: number;
|
23
|
+
/** The bounding box of this line segment. */
|
24
|
+
readonly bbox: Rect2;
|
25
|
+
/** Creates a new `LineSegment2` from its endpoints. */
|
26
|
+
constructor(point1: Point2, point2: Point2);
|
27
|
+
/** Alias for `point1`. */
|
28
|
+
get p1(): Point2;
|
29
|
+
/** Alias for `point2`. */
|
30
|
+
get p2(): Point2;
|
31
|
+
/**
|
32
|
+
* Gets a point a distance `t` along this line.
|
33
|
+
*
|
34
|
+
* @deprecated
|
35
|
+
*/
|
36
|
+
get(t: number): Point2;
|
37
|
+
/**
|
38
|
+
* Returns a point a fraction, `t`, along this line segment.
|
39
|
+
* Thus, `segment.at(0)` returns `segment.p1` and `segment.at(1)` returns
|
40
|
+
* `segment.p2`.
|
41
|
+
*
|
42
|
+
* `t` should be in `[0, 1]`.
|
43
|
+
*/
|
44
|
+
at(t: number): Point2;
|
45
|
+
intersection(other: LineSegment2): IntersectionResult | null;
|
46
|
+
intersects(other: LineSegment2): boolean;
|
47
|
+
/**
|
48
|
+
* Returns the points at which this line segment intersects the
|
49
|
+
* given line segment.
|
50
|
+
*
|
51
|
+
* Note that {@link intersects} returns *whether* this line segment intersects another
|
52
|
+
* line segment. This method, by contrast, returns **the point** at which the intersection
|
53
|
+
* occurs, if such a point exists.
|
54
|
+
*/
|
55
|
+
intersectsLineSegment(lineSegment: LineSegment2): import("../Vec3").Vec3[];
|
56
|
+
closestPointTo(target: Point2): import("../Vec3").Vec3;
|
57
|
+
/**
|
58
|
+
* Returns the distance from this line segment to `target`.
|
59
|
+
*
|
60
|
+
* Because a line segment has no interior, this signed distance is equivalent to
|
61
|
+
* the full distance between `target` and this line segment.
|
62
|
+
*/
|
63
|
+
signedDistance(target: Point2): number;
|
64
|
+
/** Returns a copy of this line segment transformed by the given `affineTransfm`. */
|
65
|
+
transformedBy(affineTransfm: Mat33): LineSegment2;
|
66
|
+
/** @inheritdoc */
|
67
|
+
getTightBoundingBox(): Rect2;
|
68
|
+
toString(): string;
|
69
|
+
}
|
70
|
+
export default LineSegment2;
|
@@ -0,0 +1,183 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
exports.LineSegment2 = void 0;
|
7
|
+
const Rect2_1 = __importDefault(require("./Rect2"));
|
8
|
+
const Vec2_1 = require("../Vec2");
|
9
|
+
const Abstract2DShape_1 = __importDefault(require("./Abstract2DShape"));
|
10
|
+
/** Represents a line segment. A `LineSegment2` is immutable. */
|
11
|
+
class LineSegment2 extends Abstract2DShape_1.default {
|
12
|
+
/** Creates a new `LineSegment2` from its endpoints. */
|
13
|
+
constructor(point1, point2) {
|
14
|
+
super();
|
15
|
+
this.point1 = point1;
|
16
|
+
this.point2 = point2;
|
17
|
+
this.bbox = Rect2_1.default.bboxOf([point1, point2]);
|
18
|
+
this.direction = point2.minus(point1);
|
19
|
+
this.length = this.direction.magnitude();
|
20
|
+
// Normalize
|
21
|
+
if (this.length > 0) {
|
22
|
+
this.direction = this.direction.times(1 / this.length);
|
23
|
+
}
|
24
|
+
}
|
25
|
+
// Accessors to make LineSegment2 compatible with bezier-js's
|
26
|
+
// interface
|
27
|
+
/** Alias for `point1`. */
|
28
|
+
get p1() {
|
29
|
+
return this.point1;
|
30
|
+
}
|
31
|
+
/** Alias for `point2`. */
|
32
|
+
get p2() {
|
33
|
+
return this.point2;
|
34
|
+
}
|
35
|
+
/**
|
36
|
+
* Gets a point a distance `t` along this line.
|
37
|
+
*
|
38
|
+
* @deprecated
|
39
|
+
*/
|
40
|
+
get(t) {
|
41
|
+
return this.point1.plus(this.direction.times(t));
|
42
|
+
}
|
43
|
+
/**
|
44
|
+
* Returns a point a fraction, `t`, along this line segment.
|
45
|
+
* Thus, `segment.at(0)` returns `segment.p1` and `segment.at(1)` returns
|
46
|
+
* `segment.p2`.
|
47
|
+
*
|
48
|
+
* `t` should be in `[0, 1]`.
|
49
|
+
*/
|
50
|
+
at(t) {
|
51
|
+
return this.get(t * this.length);
|
52
|
+
}
|
53
|
+
intersection(other) {
|
54
|
+
// We want x₁(t) = x₂(t) and y₁(t) = y₂(t)
|
55
|
+
// Observe that
|
56
|
+
// x = this.point1.x + this.direction.x · t₁
|
57
|
+
// = other.point1.x + other.direction.x · t₂
|
58
|
+
// Thus,
|
59
|
+
// t₁ = (x - this.point1.x) / this.direction.x
|
60
|
+
// = (y - this.point1.y) / this.direction.y
|
61
|
+
// and
|
62
|
+
// t₂ = (x - other.point1.x) / other.direction.x
|
63
|
+
// (and similarly for y)
|
64
|
+
//
|
65
|
+
// Letting o₁ₓ = this.point1.x, o₂ₓ = other.point1.x,
|
66
|
+
// d₁ᵧ = this.direction.y, ...
|
67
|
+
//
|
68
|
+
// We can substitute these into the equations for y:
|
69
|
+
// y = o₁ᵧ + d₁ᵧ · (x - o₁ₓ) / d₁ₓ
|
70
|
+
// = o₂ᵧ + d₂ᵧ · (x - o₂ₓ) / d₂ₓ
|
71
|
+
// ⇒ o₁ᵧ - o₂ᵧ = d₂ᵧ · (x - o₂ₓ) / d₂ₓ - d₁ᵧ · (x - o₁ₓ) / d₁ₓ
|
72
|
+
// = (d₂ᵧ/d₂ₓ)(x) - (d₂ᵧ/d₂ₓ)(o₂ₓ) - (d₁ᵧ/d₁ₓ)(x) + (d₁ᵧ/d₁ₓ)(o₁ₓ)
|
73
|
+
// = (x)(d₂ᵧ/d₂ₓ - d₁ᵧ/d₁ₓ) - (d₂ᵧ/d₂ₓ)(o₂ₓ) + (d₁ᵧ/d₁ₓ)(o₁ₓ)
|
74
|
+
// ⇒ (x)(d₂ᵧ/d₂ₓ - d₁ᵧ/d₁ₓ) = o₁ᵧ - o₂ᵧ + (d₂ᵧ/d₂ₓ)(o₂ₓ) - (d₁ᵧ/d₁ₓ)(o₁ₓ)
|
75
|
+
// ⇒ x = (o₁ᵧ - o₂ᵧ + (d₂ᵧ/d₂ₓ)(o₂ₓ) - (d₁ᵧ/d₁ₓ)(o₁ₓ))/(d₂ᵧ/d₂ₓ - d₁ᵧ/d₁ₓ)
|
76
|
+
// = (d₁ₓd₂ₓ)(o₁ᵧ - o₂ᵧ + (d₂ᵧ/d₂ₓ)(o₂ₓ) - (d₁ᵧ/d₁ₓ)(o₁ₓ))/(d₂ᵧd₁ₓ - d₁ᵧd₂ₓ)
|
77
|
+
// = ((o₁ᵧ - o₂ᵧ)((d₁ₓd₂ₓ)) + (d₂ᵧd₁ₓ)(o₂ₓ) - (d₁ᵧd₂ₓ)(o₁ₓ))/(d₂ᵧd₁ₓ - d₁ᵧd₂ₓ)
|
78
|
+
// ⇒ y = o₁ᵧ + d₁ᵧ · (x - o₁ₓ) / d₁ₓ = ...
|
79
|
+
let resultPoint, resultT;
|
80
|
+
if (this.direction.x === 0) {
|
81
|
+
// Vertical line: Where does the other have x = this.point1.x?
|
82
|
+
// x = o₁ₓ = o₂ₓ + d₂ₓ · (y - o₂ᵧ) / d₂ᵧ
|
83
|
+
// ⇒ (o₁ₓ - o₂ₓ)(d₂ᵧ/d₂ₓ) + o₂ᵧ = y
|
84
|
+
// Avoid division by zero
|
85
|
+
if (other.direction.x === 0 || this.direction.y === 0) {
|
86
|
+
return null;
|
87
|
+
}
|
88
|
+
const xIntersect = this.point1.x;
|
89
|
+
const yIntersect = (this.point1.x - other.point1.x) * other.direction.y / other.direction.x + other.point1.y;
|
90
|
+
resultPoint = Vec2_1.Vec2.of(xIntersect, yIntersect);
|
91
|
+
resultT = (yIntersect - this.point1.y) / this.direction.y;
|
92
|
+
}
|
93
|
+
else {
|
94
|
+
// From above,
|
95
|
+
// x = ((o₁ᵧ - o₂ᵧ)(d₁ₓd₂ₓ) + (d₂ᵧd₁ₓ)(o₂ₓ) - (d₁ᵧd₂ₓ)(o₁ₓ))/(d₂ᵧd₁ₓ - d₁ᵧd₂ₓ)
|
96
|
+
const numerator = ((this.point1.y - other.point1.y) * this.direction.x * other.direction.x
|
97
|
+
+ this.direction.x * other.direction.y * other.point1.x
|
98
|
+
- this.direction.y * other.direction.x * this.point1.x);
|
99
|
+
const denominator = (other.direction.y * this.direction.x
|
100
|
+
- this.direction.y * other.direction.x);
|
101
|
+
// Avoid dividing by zero. It means there is no intersection
|
102
|
+
if (denominator === 0) {
|
103
|
+
return null;
|
104
|
+
}
|
105
|
+
const xIntersect = numerator / denominator;
|
106
|
+
const t1 = (xIntersect - this.point1.x) / this.direction.x;
|
107
|
+
const yIntersect = this.point1.y + this.direction.y * t1;
|
108
|
+
resultPoint = Vec2_1.Vec2.of(xIntersect, yIntersect);
|
109
|
+
resultT = (xIntersect - this.point1.x) / this.direction.x;
|
110
|
+
}
|
111
|
+
// Ensure the result is in this/the other segment.
|
112
|
+
const resultToP1 = resultPoint.minus(this.point1).magnitude();
|
113
|
+
const resultToP2 = resultPoint.minus(this.point2).magnitude();
|
114
|
+
const resultToP3 = resultPoint.minus(other.point1).magnitude();
|
115
|
+
const resultToP4 = resultPoint.minus(other.point2).magnitude();
|
116
|
+
if (resultToP1 > this.length
|
117
|
+
|| resultToP2 > this.length
|
118
|
+
|| resultToP3 > other.length
|
119
|
+
|| resultToP4 > other.length) {
|
120
|
+
return null;
|
121
|
+
}
|
122
|
+
return {
|
123
|
+
point: resultPoint,
|
124
|
+
t: resultT,
|
125
|
+
};
|
126
|
+
}
|
127
|
+
intersects(other) {
|
128
|
+
return this.intersection(other) !== null;
|
129
|
+
}
|
130
|
+
/**
|
131
|
+
* Returns the points at which this line segment intersects the
|
132
|
+
* given line segment.
|
133
|
+
*
|
134
|
+
* Note that {@link intersects} returns *whether* this line segment intersects another
|
135
|
+
* line segment. This method, by contrast, returns **the point** at which the intersection
|
136
|
+
* occurs, if such a point exists.
|
137
|
+
*/
|
138
|
+
intersectsLineSegment(lineSegment) {
|
139
|
+
const intersection = this.intersection(lineSegment);
|
140
|
+
if (intersection) {
|
141
|
+
return [intersection.point];
|
142
|
+
}
|
143
|
+
return [];
|
144
|
+
}
|
145
|
+
// Returns the closest point on this to [target]
|
146
|
+
closestPointTo(target) {
|
147
|
+
// Distance from P1 along this' direction.
|
148
|
+
const projectedDistFromP1 = target.minus(this.p1).dot(this.direction);
|
149
|
+
const projectedDistFromP2 = this.length - projectedDistFromP1;
|
150
|
+
const projection = this.p1.plus(this.direction.times(projectedDistFromP1));
|
151
|
+
if (projectedDistFromP1 > 0 && projectedDistFromP1 < this.length) {
|
152
|
+
return projection;
|
153
|
+
}
|
154
|
+
if (Math.abs(projectedDistFromP2) < Math.abs(projectedDistFromP1)) {
|
155
|
+
return this.p2;
|
156
|
+
}
|
157
|
+
else {
|
158
|
+
return this.p1;
|
159
|
+
}
|
160
|
+
}
|
161
|
+
/**
|
162
|
+
* Returns the distance from this line segment to `target`.
|
163
|
+
*
|
164
|
+
* Because a line segment has no interior, this signed distance is equivalent to
|
165
|
+
* the full distance between `target` and this line segment.
|
166
|
+
*/
|
167
|
+
signedDistance(target) {
|
168
|
+
return this.closestPointTo(target).minus(target).magnitude();
|
169
|
+
}
|
170
|
+
/** Returns a copy of this line segment transformed by the given `affineTransfm`. */
|
171
|
+
transformedBy(affineTransfm) {
|
172
|
+
return new LineSegment2(affineTransfm.transformVec2(this.p1), affineTransfm.transformVec2(this.p2));
|
173
|
+
}
|
174
|
+
/** @inheritdoc */
|
175
|
+
getTightBoundingBox() {
|
176
|
+
return this.bbox;
|
177
|
+
}
|
178
|
+
toString() {
|
179
|
+
return `LineSegment(${this.p1.toString()}, ${this.p2.toString()})`;
|
180
|
+
}
|
181
|
+
}
|
182
|
+
exports.LineSegment2 = LineSegment2;
|
183
|
+
exports.default = LineSegment2;
|
@@ -0,0 +1,96 @@
|
|
1
|
+
import LineSegment2 from './LineSegment2';
|
2
|
+
import Mat33 from '../Mat33';
|
3
|
+
import Rect2 from './Rect2';
|
4
|
+
import { Point2 } from '../Vec2';
|
5
|
+
import Abstract2DShape from './Abstract2DShape';
|
6
|
+
export declare enum PathCommandType {
|
7
|
+
LineTo = 0,
|
8
|
+
MoveTo = 1,
|
9
|
+
CubicBezierTo = 2,
|
10
|
+
QuadraticBezierTo = 3
|
11
|
+
}
|
12
|
+
export interface CubicBezierPathCommand {
|
13
|
+
kind: PathCommandType.CubicBezierTo;
|
14
|
+
controlPoint1: Point2;
|
15
|
+
controlPoint2: Point2;
|
16
|
+
endPoint: Point2;
|
17
|
+
}
|
18
|
+
export interface QuadraticBezierPathCommand {
|
19
|
+
kind: PathCommandType.QuadraticBezierTo;
|
20
|
+
controlPoint: Point2;
|
21
|
+
endPoint: Point2;
|
22
|
+
}
|
23
|
+
export interface LinePathCommand {
|
24
|
+
kind: PathCommandType.LineTo;
|
25
|
+
point: Point2;
|
26
|
+
}
|
27
|
+
export interface MoveToPathCommand {
|
28
|
+
kind: PathCommandType.MoveTo;
|
29
|
+
point: Point2;
|
30
|
+
}
|
31
|
+
export type PathCommand = CubicBezierPathCommand | QuadraticBezierPathCommand | MoveToPathCommand | LinePathCommand;
|
32
|
+
interface IntersectionResult {
|
33
|
+
curve: Abstract2DShape;
|
34
|
+
/** @internal @deprecated */
|
35
|
+
parameterValue?: number;
|
36
|
+
point: Point2;
|
37
|
+
}
|
38
|
+
type GeometryType = Abstract2DShape;
|
39
|
+
type GeometryArrayType = Array<GeometryType>;
|
40
|
+
/**
|
41
|
+
* Represents a union of lines and curves.
|
42
|
+
*/
|
43
|
+
export declare class Path {
|
44
|
+
readonly startPoint: Point2;
|
45
|
+
readonly parts: PathCommand[];
|
46
|
+
/**
|
47
|
+
* A rough estimate of the bounding box of the path.
|
48
|
+
* A slight overestimate.
|
49
|
+
* See {@link getExactBBox}
|
50
|
+
*/
|
51
|
+
readonly bbox: Rect2;
|
52
|
+
constructor(startPoint: Point2, parts: PathCommand[]);
|
53
|
+
getExactBBox(): Rect2;
|
54
|
+
private cachedGeometry;
|
55
|
+
get geometry(): GeometryArrayType;
|
56
|
+
private cachedPolylineApproximation;
|
57
|
+
polylineApproximation(): LineSegment2[];
|
58
|
+
static computeBBoxForSegment(startPoint: Point2, part: PathCommand): Rect2;
|
59
|
+
/**
|
60
|
+
* Let `S` be a closed path a distance `strokeRadius` from this path.
|
61
|
+
*
|
62
|
+
* @returns Approximate intersections of `line` with `S` using ray marching, starting from
|
63
|
+
* both end points of `line` and each point in `additionalRaymarchStartPoints`.
|
64
|
+
*/
|
65
|
+
private raymarchIntersectionWith;
|
66
|
+
/**
|
67
|
+
* Returns a list of intersections with this path. If `strokeRadius` is given,
|
68
|
+
* intersections are approximated with the surface `strokeRadius` away from this.
|
69
|
+
*
|
70
|
+
* If `strokeRadius > 0`, the resultant `parameterValue` has no defined value.
|
71
|
+
*/
|
72
|
+
intersection(line: LineSegment2, strokeRadius?: number): IntersectionResult[];
|
73
|
+
private static mapPathCommand;
|
74
|
+
mapPoints(mapping: (point: Point2) => Point2): Path;
|
75
|
+
transformedBy(affineTransfm: Mat33): Path;
|
76
|
+
union(other: Path | null): Path;
|
77
|
+
private getEndPoint;
|
78
|
+
roughlyIntersects(rect: Rect2, strokeWidth?: number): boolean;
|
79
|
+
closedRoughlyIntersects(rect: Rect2): boolean;
|
80
|
+
static fromRect(rect: Rect2, lineWidth?: number | null): Path;
|
81
|
+
private cachedStringVersion;
|
82
|
+
toString(useNonAbsCommands?: boolean): string;
|
83
|
+
serialize(): string;
|
84
|
+
static toString(startPoint: Point2, parts: PathCommand[], onlyAbsCommands?: boolean): string;
|
85
|
+
/**
|
86
|
+
* Create a Path from a SVG path specification.
|
87
|
+
*
|
88
|
+
* ## To-do
|
89
|
+
* - TODO: Support a larger subset of SVG paths
|
90
|
+
* - Elliptical arcs are currently unsupported.
|
91
|
+
* - TODO: Support `s`,`t` commands shorthands.
|
92
|
+
*/
|
93
|
+
static fromString(pathString: string): Path;
|
94
|
+
static empty: Path;
|
95
|
+
}
|
96
|
+
export default Path;
|