@js-draw/math 1.0.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 +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;
|