@matboks/utilities 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +1 -0
- package/dist/geometry/aabb.d.ts +40 -0
- package/dist/geometry/aabb.js +143 -0
- package/dist/geometry/cut-polygon.d.ts +7 -0
- package/dist/geometry/cut-polygon.js +116 -0
- package/dist/geometry/hollowgon.d.ts +24 -0
- package/dist/geometry/hollowgon.js +156 -0
- package/dist/geometry/index.d.ts +13 -0
- package/dist/geometry/index.js +14 -0
- package/dist/geometry/line.d.ts +10 -0
- package/dist/geometry/line.js +26 -0
- package/dist/geometry/polygon-operations.d.ts +31 -0
- package/dist/geometry/polygon-operations.js +159 -0
- package/dist/geometry/polygon.d.ts +55 -0
- package/dist/geometry/polygon.js +353 -0
- package/dist/geometry/polyline.d.ts +23 -0
- package/dist/geometry/polyline.js +100 -0
- package/dist/geometry/ray.d.ts +6 -0
- package/dist/geometry/ray.js +6 -0
- package/dist/geometry/ray3.d.ts +7 -0
- package/dist/geometry/ray3.js +10 -0
- package/dist/geometry/segment.d.ts +16 -0
- package/dist/geometry/segment.js +50 -0
- package/dist/geometry/shared.d.ts +5 -0
- package/dist/geometry/shared.js +60 -0
- package/dist/geometry/transformations.d.ts +7 -0
- package/dist/geometry/transformations.js +28 -0
- package/dist/geometry/vec2.d.ts +71 -0
- package/dist/geometry/vec2.js +225 -0
- package/dist/geometry/vec3.d.ts +102 -0
- package/dist/geometry/vec3.js +256 -0
- package/dist/geometry/vec4.d.ts +71 -0
- package/dist/geometry/vec4.js +238 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +11 -0
- package/dist/math/clamp.d.ts +1 -0
- package/dist/math/clamp.js +5 -0
- package/dist/math/fract.d.ts +1 -0
- package/dist/math/fract.js +3 -0
- package/dist/math/index.d.ts +8 -0
- package/dist/math/index.js +8 -0
- package/dist/math/mix.d.ts +1 -0
- package/dist/math/mix.js +3 -0
- package/dist/math/mod.d.ts +1 -0
- package/dist/math/mod.js +5 -0
- package/dist/math/signed.d.ts +1 -0
- package/dist/math/signed.js +3 -0
- package/dist/math/smoothstep.d.ts +3 -0
- package/dist/math/smoothstep.js +24 -0
- package/dist/math/split-into-int-and-fract.d.ts +1 -0
- package/dist/math/split-into-int-and-fract.js +5 -0
- package/dist/math/units.d.ts +2 -0
- package/dist/math/units.js +6 -0
- package/dist/noise/idw.d.ts +11 -0
- package/dist/noise/idw.js +10 -0
- package/dist/noise/index.d.ts +6 -0
- package/dist/noise/index.js +6 -0
- package/dist/noise/perlin.d.ts +5 -0
- package/dist/noise/perlin.js +29 -0
- package/dist/noise/random.d.ts +13 -0
- package/dist/noise/random.js +39 -0
- package/dist/noise/value.d.ts +4 -0
- package/dist/noise/value.js +20 -0
- package/dist/noise/voronoise.d.ts +10 -0
- package/dist/noise/voronoise.js +26 -0
- package/dist/noise/worley.d.ts +4 -0
- package/dist/noise/worley.js +39 -0
- package/dist/shader-modules/index.d.ts +2 -0
- package/dist/shader-modules/index.js +2 -0
- package/dist/shader-modules/library.d.ts +2 -0
- package/dist/shader-modules/library.js +36 -0
- package/dist/shader-modules/modules/camera.d.ts +1 -0
- package/dist/shader-modules/modules/camera.js +43 -0
- package/dist/shader-modules/modules/color/blend.d.ts +1 -0
- package/dist/shader-modules/modules/color/blend.js +108 -0
- package/dist/shader-modules/modules/color/index.d.ts +1 -0
- package/dist/shader-modules/modules/color/index.js +135 -0
- package/dist/shader-modules/modules/constants.d.ts +1 -0
- package/dist/shader-modules/modules/constants.js +14 -0
- package/dist/shader-modules/modules/geometry.d.ts +1 -0
- package/dist/shader-modules/modules/geometry.js +110 -0
- package/dist/shader-modules/modules/math.d.ts +1 -0
- package/dist/shader-modules/modules/math.js +19 -0
- package/dist/shader-modules/modules/noise.d.ts +1 -0
- package/dist/shader-modules/modules/noise.js +410 -0
- package/dist/shader-modules/modules/random.d.ts +1 -0
- package/dist/shader-modules/modules/random.js +147 -0
- package/dist/shader-modules/modules/ray-marching.d.ts +1 -0
- package/dist/shader-modules/modules/ray-marching.js +54 -0
- package/dist/shader-modules/modules/sdf/index.d.ts +1 -0
- package/dist/shader-modules/modules/sdf/index.js +183 -0
- package/dist/shader-modules/modules/sdf/operations.d.ts +1 -0
- package/dist/shader-modules/modules/sdf/operations.js +77 -0
- package/dist/shader-modules/modules/utils.d.ts +1 -0
- package/dist/shader-modules/modules/utils.js +115 -0
- package/dist/shader-modules/registry.d.ts +2 -0
- package/dist/shader-modules/registry.js +7 -0
- package/dist/shader-modules/shaders.d.ts +1 -0
- package/dist/shader-modules/shaders.js +109 -0
- package/dist/shader-renderer/helpers.d.ts +10 -0
- package/dist/shader-renderer/helpers.js +19 -0
- package/dist/shader-renderer/index.d.ts +2 -0
- package/dist/shader-renderer/index.js +2 -0
- package/dist/shader-renderer/pixel-shader.d.ts +32 -0
- package/dist/shader-renderer/pixel-shader.js +172 -0
- package/dist/shader-renderer/pixel-vertex-shader.d.ts +1 -0
- package/dist/shader-renderer/pixel-vertex-shader.js +14 -0
- package/dist/shader-renderer/texture.d.ts +28 -0
- package/dist/shader-renderer/texture.js +97 -0
- package/dist/shader-renderer/types.d.ts +17 -0
- package/dist/shader-renderer/types.js +4 -0
- package/dist/shader-renderer/uniform.d.ts +16 -0
- package/dist/shader-renderer/uniform.js +134 -0
- package/dist/tilings/delaunay.d.ts +11 -0
- package/dist/tilings/delaunay.js +28 -0
- package/dist/tilings/grid.d.ts +5 -0
- package/dist/tilings/grid.js +29 -0
- package/dist/tilings/hexagononal.d.ts +4 -0
- package/dist/tilings/hexagononal.js +40 -0
- package/dist/tilings/index.d.ts +12 -0
- package/dist/tilings/index.js +12 -0
- package/dist/tilings/line.d.ts +5 -0
- package/dist/tilings/line.js +19 -0
- package/dist/tilings/mediterranean.d.ts +3 -0
- package/dist/tilings/mediterranean.js +49 -0
- package/dist/tilings/pythagorean.d.ts +3 -0
- package/dist/tilings/pythagorean.js +40 -0
- package/dist/tilings/random-cuts.d.ts +6 -0
- package/dist/tilings/random-cuts.js +16 -0
- package/dist/tilings/recursive-cuts.d.ts +5 -0
- package/dist/tilings/recursive-cuts.js +11 -0
- package/dist/tilings/rhombille.d.ts +3 -0
- package/dist/tilings/rhombille.js +16 -0
- package/dist/tilings/rightangled-triangle.d.ts +3 -0
- package/dist/tilings/rightangled-triangle.js +29 -0
- package/dist/tilings/triangle.d.ts +3 -0
- package/dist/tilings/triangle.js +17 -0
- package/dist/tilings/voronoi.d.ts +11 -0
- package/dist/tilings/voronoi.js +29 -0
- package/dist/tilings/weaving.d.ts +1 -0
- package/dist/tilings/weaving.js +8 -0
- package/dist/transforms/camera/camera.d.ts +32 -0
- package/dist/transforms/camera/camera.js +60 -0
- package/dist/transforms/camera/index.d.ts +3 -0
- package/dist/transforms/camera/index.js +3 -0
- package/dist/transforms/camera/orthographic.d.ts +15 -0
- package/dist/transforms/camera/orthographic.js +31 -0
- package/dist/transforms/camera/perspective.d.ts +16 -0
- package/dist/transforms/camera/perspective.js +40 -0
- package/dist/transforms/index.d.ts +2 -0
- package/dist/transforms/index.js +2 -0
- package/dist/transforms/normalize-transform.d.ts +12 -0
- package/dist/transforms/normalize-transform.js +20 -0
- package/dist/types.d.ts +1 -0
- package/dist/types.js +1 -0
- package/dist/utilities/create-frame.d.ts +2 -0
- package/dist/utilities/create-frame.js +6 -0
- package/dist/utilities/ensure-array.d.ts +1 -0
- package/dist/utilities/ensure-array.js +3 -0
- package/dist/utilities/idw-interpolator.d.ts +9 -0
- package/dist/utilities/idw-interpolator.js +18 -0
- package/dist/utilities/index.d.ts +8 -0
- package/dist/utilities/index.js +8 -0
- package/dist/utilities/marching-squares.d.ts +51 -0
- package/dist/utilities/marching-squares.js +177 -0
- package/dist/utilities/point-sampler.d.ts +12 -0
- package/dist/utilities/point-sampler.js +24 -0
- package/dist/utilities/poisson/grid.d.ts +21 -0
- package/dist/utilities/poisson/grid.js +51 -0
- package/dist/utilities/poisson/poissonnier.d.ts +50 -0
- package/dist/utilities/poisson/poissonnier.js +118 -0
- package/dist/utilities/resolution.d.ts +10 -0
- package/dist/utilities/resolution.js +14 -0
- package/dist/utilities/rng.d.ts +13 -0
- package/dist/utilities/rng.js +80 -0
- package/package.json +28 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const EPS = 1e-9;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const EPS = 1e-9;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Line } from "./line.js";
|
|
2
|
+
import { Polygon } from "./polygon.js";
|
|
3
|
+
import { Vec2 } from "./vec2.js";
|
|
4
|
+
export declare class AABB {
|
|
5
|
+
xMin: number;
|
|
6
|
+
xMax: number;
|
|
7
|
+
yMin: number;
|
|
8
|
+
yMax: number;
|
|
9
|
+
constructor(xMin: number, xMax: number, yMin: number, yMax: number);
|
|
10
|
+
get width(): number;
|
|
11
|
+
get height(): number;
|
|
12
|
+
get center(): Vec2;
|
|
13
|
+
get diagonal(): number;
|
|
14
|
+
get size(): Vec2;
|
|
15
|
+
get halfSize(): Vec2;
|
|
16
|
+
get aspectRatio(): number;
|
|
17
|
+
get area(): number;
|
|
18
|
+
translate(translation: Vec2): this;
|
|
19
|
+
static fromVertices(vertices: Vec2[]): AABB;
|
|
20
|
+
static fromPoint({ x, y }: Vec2): AABB;
|
|
21
|
+
static fromCenterSize(center: Vec2, size: Vec2): AABB;
|
|
22
|
+
static placeholder(): AABB;
|
|
23
|
+
clone(): AABB;
|
|
24
|
+
signedDistanceTo(position: Vec2): number;
|
|
25
|
+
contains(other: AABB): boolean;
|
|
26
|
+
isOverlapping(other: AABB): boolean;
|
|
27
|
+
isInside(other: AABB): boolean;
|
|
28
|
+
intersectsLine(line: Line): boolean;
|
|
29
|
+
grow(amount: number): AABB;
|
|
30
|
+
grow(amount: Vec2): AABB;
|
|
31
|
+
grow(xMin: number, xMax: number, yMin: number, yMax: number): AABB;
|
|
32
|
+
combine(other: AABB): this;
|
|
33
|
+
update(position: Vec2): this;
|
|
34
|
+
isPointInside({ x, y }: Vec2): boolean;
|
|
35
|
+
setBounds(xMin: number, xMax: number, yMin: number, yMax: number): this;
|
|
36
|
+
mapToUV(p: Vec2): Vec2;
|
|
37
|
+
mapFromUV(p: Vec2): Vec2;
|
|
38
|
+
toPolygon(): Polygon;
|
|
39
|
+
toString(): string;
|
|
40
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { EPS } from "../constants.js";
|
|
2
|
+
import { Polygon } from "./polygon.js";
|
|
3
|
+
import { vec2, Vec2 } from "./vec2.js";
|
|
4
|
+
const { min, max } = Math;
|
|
5
|
+
export class AABB {
|
|
6
|
+
constructor(xMin, xMax, yMin, yMax) {
|
|
7
|
+
this.xMin = xMin;
|
|
8
|
+
this.xMax = xMax;
|
|
9
|
+
this.yMin = yMin;
|
|
10
|
+
this.yMax = yMax;
|
|
11
|
+
}
|
|
12
|
+
get width() { return this.xMax - this.xMin; }
|
|
13
|
+
get height() { return this.yMax - this.yMin; }
|
|
14
|
+
get center() { return vec2((this.xMin + this.xMax) / 2, (this.yMin + this.yMax) / 2); }
|
|
15
|
+
get diagonal() { return this.size.length(); }
|
|
16
|
+
get size() { return vec2((this.xMax - this.xMin), (this.yMax - this.yMin)); }
|
|
17
|
+
get halfSize() { return Vec2.multiply(this.size, 0.5); }
|
|
18
|
+
get aspectRatio() { return (this.xMax - this.xMin) / (this.yMax - this.yMin); }
|
|
19
|
+
get area() { return (this.xMax - this.xMin) * (this.yMax - this.yMin); }
|
|
20
|
+
translate(translation) {
|
|
21
|
+
const { x, y } = translation;
|
|
22
|
+
const { xMin, xMax, yMin, yMax } = this;
|
|
23
|
+
return this.setBounds(xMin + x, xMax + x, yMin + y, yMax + y);
|
|
24
|
+
}
|
|
25
|
+
static fromVertices(vertices) {
|
|
26
|
+
let { xMin, xMax, yMin, yMax } = AABB.placeholder();
|
|
27
|
+
for (let i = 0; i < vertices.length; i++) {
|
|
28
|
+
const { x, y } = vertices[i];
|
|
29
|
+
if (x < xMin)
|
|
30
|
+
xMin = x;
|
|
31
|
+
if (x > xMax)
|
|
32
|
+
xMax = x;
|
|
33
|
+
if (y < yMin)
|
|
34
|
+
yMin = y;
|
|
35
|
+
if (y > yMax)
|
|
36
|
+
yMax = y;
|
|
37
|
+
}
|
|
38
|
+
return new AABB(xMin, xMax, yMin, yMax);
|
|
39
|
+
}
|
|
40
|
+
static fromPoint({ x, y }) {
|
|
41
|
+
return new AABB(x, x, y, y);
|
|
42
|
+
}
|
|
43
|
+
static fromCenterSize(center, size) {
|
|
44
|
+
return new AABB(center.x - size.x / 2, center.x + size.x / 2, center.y - size.y / 2, center.y + size.y / 2);
|
|
45
|
+
}
|
|
46
|
+
static placeholder() {
|
|
47
|
+
return new AABB(Infinity, -Infinity, Infinity, -Infinity);
|
|
48
|
+
}
|
|
49
|
+
clone() {
|
|
50
|
+
return new AABB(this.xMin, this.xMax, this.yMin, this.yMax);
|
|
51
|
+
}
|
|
52
|
+
signedDistanceTo(position) {
|
|
53
|
+
// Fix AABB
|
|
54
|
+
const difference = Vec2.subtract(position, this.center).absolute().minus(this.halfSize);
|
|
55
|
+
if (difference.x < 0 && difference.y < 0) {
|
|
56
|
+
return max(difference.x, difference.y);
|
|
57
|
+
}
|
|
58
|
+
else if (difference.x > 0 && difference.y > 0) {
|
|
59
|
+
return difference.length();
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
return max(difference.x, difference.y);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
contains(other) {
|
|
66
|
+
return this.xMin < other.xMin + EPS && this.xMax > other.xMax - EPS && this.yMin < other.yMin + EPS && this.yMax > other.yMax - EPS;
|
|
67
|
+
}
|
|
68
|
+
isOverlapping(other) {
|
|
69
|
+
return !(this.xMax < other.xMin || this.xMin > other.xMax || this.yMax < other.yMin || this.yMin > other.yMax);
|
|
70
|
+
}
|
|
71
|
+
isInside(other) {
|
|
72
|
+
return this.xMin >= other.xMin && this.xMax <= other.xMax && this.yMin >= other.yMin && this.yMax <= other.yMax;
|
|
73
|
+
}
|
|
74
|
+
// Checks if all 4 corners of AABB are on the same side of line
|
|
75
|
+
intersectsLine(line) {
|
|
76
|
+
const { point, direction } = line;
|
|
77
|
+
const polygon = this.toPolygon();
|
|
78
|
+
let sum = 0;
|
|
79
|
+
for (const vertex of polygon.vertices) {
|
|
80
|
+
sum += Math.sign(Vec2.subtract(vertex, point).cross(direction));
|
|
81
|
+
}
|
|
82
|
+
return Math.abs(sum) !== 4;
|
|
83
|
+
}
|
|
84
|
+
grow(...args) {
|
|
85
|
+
const { xMin, xMax, yMin, yMax, width, height } = this;
|
|
86
|
+
if (args.length === 1) {
|
|
87
|
+
let amount = args[0];
|
|
88
|
+
amount = typeof amount === "number" ? vec2(amount) : amount;
|
|
89
|
+
amount = vec2(max(amount.x, -width / 2), max(amount.y, -height / 2));
|
|
90
|
+
return this.setBounds(xMin - amount.x, xMax + amount.x, yMin - amount.y, yMax + amount.y);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
const [dxMin, dxMax, dyMin, dyMax] = args;
|
|
94
|
+
let newXMin = xMin + dxMin;
|
|
95
|
+
let newXMax = xMax + dxMax;
|
|
96
|
+
let newYMin = yMin + dyMin;
|
|
97
|
+
let newYMax = yMax + dyMax;
|
|
98
|
+
if (newXMin > newXMax)
|
|
99
|
+
newXMin = newXMax = 0.5 * (newXMin + newXMax);
|
|
100
|
+
if (newYMin > newYMax)
|
|
101
|
+
newYMin = newYMax = 0.5 * (newYMin + newYMax);
|
|
102
|
+
return this.setBounds(newXMin, newXMax, newYMin, newYMax);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
combine(other) {
|
|
106
|
+
return this.setBounds(min(this.xMin, other.xMin), max(this.xMax, other.xMax), min(this.yMin, other.yMin), max(this.yMax, other.yMax));
|
|
107
|
+
}
|
|
108
|
+
update(position) {
|
|
109
|
+
return this.setBounds(min(this.xMin, position.x), max(this.xMax, position.x), min(this.yMin, position.y), max(this.yMax, position.y));
|
|
110
|
+
}
|
|
111
|
+
isPointInside({ x, y }) {
|
|
112
|
+
const { xMin, xMax, yMin, yMax } = this;
|
|
113
|
+
return x >= xMin - EPS && x <= xMax + EPS && y >= yMin - EPS && y <= yMax + EPS;
|
|
114
|
+
}
|
|
115
|
+
setBounds(xMin, xMax, yMin, yMax) {
|
|
116
|
+
this.xMin = xMin;
|
|
117
|
+
this.xMax = xMax;
|
|
118
|
+
this.yMin = yMin;
|
|
119
|
+
this.yMax = yMax;
|
|
120
|
+
return this;
|
|
121
|
+
}
|
|
122
|
+
mapToUV(p) {
|
|
123
|
+
const { xMin, yMin, width, height } = this;
|
|
124
|
+
return p.clone().minus(vec2(xMin, yMin)).times(vec2(1 / width, 1 / height));
|
|
125
|
+
}
|
|
126
|
+
mapFromUV(p) {
|
|
127
|
+
const { xMin, yMin, width, height } = this;
|
|
128
|
+
return p.clone().times(vec2(width, height)).plus(vec2(xMin, yMin));
|
|
129
|
+
}
|
|
130
|
+
toPolygon() {
|
|
131
|
+
const { xMin, xMax, yMin, yMax } = this;
|
|
132
|
+
return new Polygon([
|
|
133
|
+
vec2(xMin, yMin),
|
|
134
|
+
vec2(xMax, yMin),
|
|
135
|
+
vec2(xMax, yMax),
|
|
136
|
+
vec2(xMin, yMax)
|
|
137
|
+
]);
|
|
138
|
+
}
|
|
139
|
+
toString() {
|
|
140
|
+
const { xMin, xMax, yMin, yMax } = this;
|
|
141
|
+
return `AABB(xMin=${xMin}, xMax=${xMax}, yMin=${yMin}, yMax=${yMax})`;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Line } from "./line.js";
|
|
2
|
+
import { Polygon } from "./polygon.js";
|
|
3
|
+
import { Ray } from "./ray.js";
|
|
4
|
+
import { Segment } from "./segment.js";
|
|
5
|
+
type CutObject = Line | Segment | Ray;
|
|
6
|
+
export declare function cutPolygon(polygons: Polygon | Array<Polygon>, cutObject: CutObject | ((polygon: Polygon) => CutObject)): Polygon[];
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { EPS } from "../constants.js";
|
|
2
|
+
import { mod } from "../math/mod.js";
|
|
3
|
+
import { Line } from "./line.js";
|
|
4
|
+
import { Polygon } from "./polygon.js";
|
|
5
|
+
import { Ray } from "./ray.js";
|
|
6
|
+
import { Segment } from "./segment.js";
|
|
7
|
+
import { computeLineIntersection } from "./shared.js";
|
|
8
|
+
// TODO add AABB check
|
|
9
|
+
export function cutPolygon(polygons, cutObject) {
|
|
10
|
+
polygons = Array.isArray(polygons) ? polygons : [polygons];
|
|
11
|
+
let cutPolygons = [];
|
|
12
|
+
for (const polygon of polygons) {
|
|
13
|
+
const polygonCutObject = typeof cutObject === "function" ? cutObject(polygon) : cutObject;
|
|
14
|
+
cutSinglePolygonHelper(polygon, polygonCutObject, cutPolygons);
|
|
15
|
+
}
|
|
16
|
+
return cutPolygons;
|
|
17
|
+
}
|
|
18
|
+
function cutSinglePolygonHelper(polygon, cutObject, allPolygons) {
|
|
19
|
+
const cutLine = cutObject instanceof Segment ? new Line(cutObject.start, cutObject.vector) :
|
|
20
|
+
cutObject instanceof Ray ? new Line(cutObject.point, cutObject.direction) :
|
|
21
|
+
cutObject;
|
|
22
|
+
// if (!polygon.aabb().intersectsLine(cutLine)) {
|
|
23
|
+
// allPolygons.push(polygon);
|
|
24
|
+
// return;
|
|
25
|
+
// }
|
|
26
|
+
let intersections = [];
|
|
27
|
+
for (let i = 0, n = polygon.numVertices(); i < n; i++) {
|
|
28
|
+
const edge = polygon.getEdge(i);
|
|
29
|
+
const intersection = computeLineIntersection(cutLine, new Line(edge.start, edge.vector));
|
|
30
|
+
if (!intersection)
|
|
31
|
+
continue;
|
|
32
|
+
const [lineTime, edgeTime] = intersection;
|
|
33
|
+
if (edgeTime < -EPS || edgeTime > 1 + EPS)
|
|
34
|
+
continue;
|
|
35
|
+
const position = edge.interpolate(edgeTime);
|
|
36
|
+
intersections.push({ index: i, position, lineTime, edgeTime });
|
|
37
|
+
}
|
|
38
|
+
if (cutObject instanceof Ray) { // Ray: ensure only intersections
|
|
39
|
+
intersections = intersections.filter((int) => {
|
|
40
|
+
return int.lineTime >= 0;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
else if (cutObject instanceof Segment) {
|
|
44
|
+
intersections = intersections.filter((int) => {
|
|
45
|
+
const { lineTime } = int;
|
|
46
|
+
return lineTime >= 0 && lineTime <= 1;
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
intersections.sort((a, b) => a.lineTime - b.lineTime);
|
|
50
|
+
// If cut object is not a line and the start point is inside the polygon,
|
|
51
|
+
// the first intersection is invalid
|
|
52
|
+
if (!(cutObject instanceof Line) && polygon.isPointInside(cutLine.point)) {
|
|
53
|
+
intersections.shift();
|
|
54
|
+
}
|
|
55
|
+
intersections = intersections.filter((int, i) => {
|
|
56
|
+
if (i === intersections.length - 1)
|
|
57
|
+
return true;
|
|
58
|
+
const nextIntersection = intersections[i + 1];
|
|
59
|
+
const timeDifference = nextIntersection.lineTime - int.lineTime;
|
|
60
|
+
if (timeDifference > EPS)
|
|
61
|
+
return true;
|
|
62
|
+
return Math.abs(nextIntersection.index - int.index) <= 1;
|
|
63
|
+
});
|
|
64
|
+
const vertices = polygon.vertices;
|
|
65
|
+
for (let i = 0; i < vertices.length; i++) {
|
|
66
|
+
vertices[i]._originalIndex = i;
|
|
67
|
+
}
|
|
68
|
+
const result = intersectionHelper(vertices, 0, intersections);
|
|
69
|
+
for (const vertices of result) {
|
|
70
|
+
allPolygons.push(new Polygon(vertices.map((vertex) => vertex.clone())));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// TODO consider rewriting function to be non-recursive
|
|
74
|
+
function intersectionHelper(vertices, intersectionIndex, intersections) {
|
|
75
|
+
const activeIntersections = [];
|
|
76
|
+
// We are at the end of the intersection array, simply return shape
|
|
77
|
+
if (intersectionIndex >= intersections.length - 1)
|
|
78
|
+
return [vertices];
|
|
79
|
+
// Find the next two intersections that happen along the vertices
|
|
80
|
+
while (activeIntersections.length < 2 && intersectionIndex < intersections.length) {
|
|
81
|
+
const candidate = intersections[intersectionIndex];
|
|
82
|
+
const index = vertices.findIndex((data) => data._originalIndex === candidate.index);
|
|
83
|
+
if (index !== -1)
|
|
84
|
+
activeIntersections.push({ index, intersection: candidate });
|
|
85
|
+
intersectionIndex++;
|
|
86
|
+
}
|
|
87
|
+
const [intersectionData1, intersectionData2] = activeIntersections;
|
|
88
|
+
if (!(intersectionData1 && intersectionData2))
|
|
89
|
+
return [vertices];
|
|
90
|
+
// Split vertices into two sets, based on which sides of the cut line they lie
|
|
91
|
+
const { vertices1, vertices2 } = splitVertices(vertices, intersectionData1.index, intersectionData2.index);
|
|
92
|
+
const { position: position1, edgeTime: edgeTime1 } = intersectionData1.intersection;
|
|
93
|
+
const { position: position2, edgeTime: edgeTime2 } = intersectionData2.intersection;
|
|
94
|
+
const newVertices1 = (edgeTime1 < (1 - EPS) ? [position1] : []) // If false, position1 coindices with the first vertex in vertices1
|
|
95
|
+
.concat(vertices1)
|
|
96
|
+
.concat(edgeTime2 > EPS ? [position2] : []); // If false, position2 coindices with the last vertex in vertices1
|
|
97
|
+
const newVertices2 = (edgeTime2 < (1 - EPS) ? [position2] : []) // If false, position2 coindices with the first vertex in vertices2
|
|
98
|
+
.concat(vertices2)
|
|
99
|
+
.concat(edgeTime1 > EPS ? [position1] : []); // If false, position1 coindices with the last vertex in vertices2
|
|
100
|
+
const result1 = newVertices1.length > 2 ? intersectionHelper(newVertices1, intersectionIndex, intersections) : [];
|
|
101
|
+
const result2 = newVertices2.length > 2 ? intersectionHelper(newVertices2, intersectionIndex, intersections) : [];
|
|
102
|
+
return result1.concat(result2);
|
|
103
|
+
}
|
|
104
|
+
function splitVertices(vertices, startIndex, endIndex) {
|
|
105
|
+
const numVertices = vertices.length;
|
|
106
|
+
let vertices1 = [];
|
|
107
|
+
let vertices2 = [];
|
|
108
|
+
const diff = startIndex < endIndex ? endIndex - startIndex : (numVertices - (startIndex - endIndex));
|
|
109
|
+
for (let i = 0; i < diff; i++) {
|
|
110
|
+
vertices1.push(vertices[mod(startIndex + 1 + i, numVertices)]);
|
|
111
|
+
}
|
|
112
|
+
for (let i = 0, n = numVertices - diff; i < n; i++) {
|
|
113
|
+
vertices2.push(vertices[mod(endIndex + 1 + i, numVertices)]);
|
|
114
|
+
}
|
|
115
|
+
return { vertices1, vertices2 };
|
|
116
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { IntersectionObject, Polygon } from "./polygon.js";
|
|
2
|
+
import { Vec2 } from "./vec2.js";
|
|
3
|
+
export declare class Hollowgon {
|
|
4
|
+
#private;
|
|
5
|
+
boundary: Polygon;
|
|
6
|
+
holes: Polygon[];
|
|
7
|
+
constructor(boundary: Polygon, holes?: Polygon[], ensureCorrectOrientation?: boolean);
|
|
8
|
+
centroid(): Vec2;
|
|
9
|
+
rotate(rotation: number, center?: Vec2): this;
|
|
10
|
+
toSimplePolygons(): Polygon;
|
|
11
|
+
area(): number;
|
|
12
|
+
aabb(): import("./aabb.js").AABB;
|
|
13
|
+
clone(): Hollowgon;
|
|
14
|
+
translate(translation: Vec2): this;
|
|
15
|
+
isPointInside(position: Vec2): boolean;
|
|
16
|
+
signedDistanceToBoundary(position: Vec2): number;
|
|
17
|
+
signedDistanceToHole(position: Vec2): number;
|
|
18
|
+
signedDistanceTo(position: Vec2): number;
|
|
19
|
+
addHole(hole: Polygon, ensureCorrectOrientation?: boolean): void;
|
|
20
|
+
tessellate(): Polygon[];
|
|
21
|
+
render(ctx: CanvasRenderingContext2D, beginPath?: boolean): void;
|
|
22
|
+
computeIntersections(intersectionObject: IntersectionObject): import("./polygon.js").PolygonIntersection[];
|
|
23
|
+
toArray(): [number, number][][];
|
|
24
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
2
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
3
|
+
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");
|
|
4
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
|
+
};
|
|
6
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
7
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
9
|
+
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");
|
|
10
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
11
|
+
};
|
|
12
|
+
var _Hollowgon_instances, _Hollowgon_area, _Hollowgon_computeArea;
|
|
13
|
+
import earcut from "earcut";
|
|
14
|
+
import { Polygon } from "./polygon.js";
|
|
15
|
+
import { Vec2 } from "./vec2.js";
|
|
16
|
+
const { abs } = Math;
|
|
17
|
+
export class Hollowgon {
|
|
18
|
+
constructor(boundary, holes, ensureCorrectOrientation = true) {
|
|
19
|
+
_Hollowgon_instances.add(this);
|
|
20
|
+
this.holes = [];
|
|
21
|
+
_Hollowgon_area.set(this, void 0);
|
|
22
|
+
if (ensureCorrectOrientation)
|
|
23
|
+
boundary.makeCounterClockwise();
|
|
24
|
+
this.boundary = boundary;
|
|
25
|
+
if (holes)
|
|
26
|
+
for (const hole of holes)
|
|
27
|
+
this.addHole(hole);
|
|
28
|
+
}
|
|
29
|
+
centroid() {
|
|
30
|
+
const { boundary, holes } = this;
|
|
31
|
+
const centroid = boundary.centroid().clone().times(boundary.area());
|
|
32
|
+
let areaSum = boundary.area();
|
|
33
|
+
for (const hole of holes) {
|
|
34
|
+
centroid.minus(hole.centroid().times(hole.area()));
|
|
35
|
+
areaSum -= hole.area();
|
|
36
|
+
}
|
|
37
|
+
return centroid.times(1 / areaSum);
|
|
38
|
+
}
|
|
39
|
+
rotate(rotation, center) {
|
|
40
|
+
center ?? (center = this.centroid());
|
|
41
|
+
this.boundary.rotate(rotation, center);
|
|
42
|
+
for (const hole of this.holes) {
|
|
43
|
+
hole.rotate(rotation, center);
|
|
44
|
+
}
|
|
45
|
+
return this;
|
|
46
|
+
}
|
|
47
|
+
toSimplePolygons() {
|
|
48
|
+
const result = this.boundary.clone();
|
|
49
|
+
for (const hole of this.holes)
|
|
50
|
+
result.addHole(hole);
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
area() { return __classPrivateFieldSet(this, _Hollowgon_area, __classPrivateFieldGet(this, _Hollowgon_area, "f") ?? __classPrivateFieldGet(this, _Hollowgon_instances, "m", _Hollowgon_computeArea).call(this), "f"); }
|
|
54
|
+
aabb() { return this.boundary.aabb(); }
|
|
55
|
+
clone() {
|
|
56
|
+
const { boundary, holes } = this;
|
|
57
|
+
const clone = new Hollowgon(boundary.clone());
|
|
58
|
+
for (const hole of holes)
|
|
59
|
+
clone.addHole(hole.clone());
|
|
60
|
+
return clone;
|
|
61
|
+
}
|
|
62
|
+
translate(translation) {
|
|
63
|
+
this.boundary.translate(translation);
|
|
64
|
+
for (const hole of this.holes)
|
|
65
|
+
hole.translate(translation);
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
isPointInside(position) {
|
|
69
|
+
const { boundary, holes } = this;
|
|
70
|
+
if (!boundary.isPointInside(position))
|
|
71
|
+
return false;
|
|
72
|
+
for (const hole of holes) {
|
|
73
|
+
if (hole.isPointInside(position))
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
signedDistanceToBoundary(position) {
|
|
79
|
+
return this.boundary.signedDistanceToBoundary(position);
|
|
80
|
+
}
|
|
81
|
+
signedDistanceToHole(position) {
|
|
82
|
+
let distance = Infinity;
|
|
83
|
+
for (const hole of this.holes) {
|
|
84
|
+
const holeDistance = -hole.signedDistanceToBoundary(position);
|
|
85
|
+
if (abs(holeDistance) < abs(distance))
|
|
86
|
+
distance = holeDistance;
|
|
87
|
+
}
|
|
88
|
+
return distance;
|
|
89
|
+
}
|
|
90
|
+
signedDistanceTo(position) {
|
|
91
|
+
const boundaryDistance = this.signedDistanceToBoundary(position);
|
|
92
|
+
const holeDistance = this.signedDistanceToHole(position);
|
|
93
|
+
return abs(boundaryDistance) < abs(holeDistance) ? boundaryDistance : holeDistance;
|
|
94
|
+
}
|
|
95
|
+
addHole(hole, ensureCorrectOrientation = true) {
|
|
96
|
+
if (ensureCorrectOrientation)
|
|
97
|
+
hole.makeClockwise();
|
|
98
|
+
this.holes.push(hole);
|
|
99
|
+
}
|
|
100
|
+
tessellate() {
|
|
101
|
+
const vertices = [];
|
|
102
|
+
const holeIndices = [];
|
|
103
|
+
vertices.push(...this.boundary.vertices);
|
|
104
|
+
for (const hole of this.holes) {
|
|
105
|
+
holeIndices.push(vertices.length);
|
|
106
|
+
vertices.push(...hole.vertices);
|
|
107
|
+
}
|
|
108
|
+
const indices = earcut(Vec2.toBuffer(vertices), holeIndices);
|
|
109
|
+
let triangles = [];
|
|
110
|
+
for (let i = 0; i < indices.length;) {
|
|
111
|
+
const triangle = new Polygon([
|
|
112
|
+
vertices[indices[i++]].clone(),
|
|
113
|
+
vertices[indices[i++]].clone(),
|
|
114
|
+
vertices[indices[i++]].clone()
|
|
115
|
+
]);
|
|
116
|
+
triangles.push(triangle);
|
|
117
|
+
}
|
|
118
|
+
return triangles;
|
|
119
|
+
}
|
|
120
|
+
render(ctx, beginPath = true) {
|
|
121
|
+
const { boundary, holes } = this;
|
|
122
|
+
if (beginPath)
|
|
123
|
+
ctx.beginPath();
|
|
124
|
+
renderHelper(ctx, boundary);
|
|
125
|
+
for (const hole of holes)
|
|
126
|
+
renderHelper(ctx, hole);
|
|
127
|
+
ctx.closePath();
|
|
128
|
+
}
|
|
129
|
+
computeIntersections(intersectionObject) {
|
|
130
|
+
let intersections = this.boundary.computeIntersections(intersectionObject);
|
|
131
|
+
for (const hole of this.holes) {
|
|
132
|
+
intersections = intersections.concat(hole.computeIntersections(intersectionObject));
|
|
133
|
+
}
|
|
134
|
+
intersections.sort((a, b) => a.lineTime - b.lineTime);
|
|
135
|
+
return intersections;
|
|
136
|
+
}
|
|
137
|
+
toArray() {
|
|
138
|
+
const vertexArray = [this.boundary.toArray()];
|
|
139
|
+
for (const hole of this.holes)
|
|
140
|
+
vertexArray.push(hole.toArray());
|
|
141
|
+
return vertexArray;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
_Hollowgon_area = new WeakMap(), _Hollowgon_instances = new WeakSet(), _Hollowgon_computeArea = function _Hollowgon_computeArea() {
|
|
145
|
+
let area = this.boundary.area();
|
|
146
|
+
for (const hole of this.holes)
|
|
147
|
+
area -= hole.area();
|
|
148
|
+
return area;
|
|
149
|
+
};
|
|
150
|
+
function renderHelper(ctx, polygon) {
|
|
151
|
+
for (let i = 0, n = polygon.numVertices(); i <= n; i++) {
|
|
152
|
+
const { x, y } = polygon.getVertex(i);
|
|
153
|
+
i === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
|
|
154
|
+
}
|
|
155
|
+
ctx.closePath();
|
|
156
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export * from "./aabb.js";
|
|
2
|
+
export * from "./line.js";
|
|
3
|
+
export * from "./polygon.js";
|
|
4
|
+
export * from "./ray.js";
|
|
5
|
+
export * from "./segment.js";
|
|
6
|
+
export * from "./vec2.js";
|
|
7
|
+
export * from "./vec3.js";
|
|
8
|
+
export * from "./vec4.js";
|
|
9
|
+
export * from "./transformations.js";
|
|
10
|
+
export * from "./hollowgon.js";
|
|
11
|
+
export * from "./polyline.js";
|
|
12
|
+
export * from "./polygon-operations.js";
|
|
13
|
+
export * from "./ray3.js";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export * from "./aabb.js";
|
|
2
|
+
// export * from "./cut-polygon.js";
|
|
3
|
+
export * from "./line.js";
|
|
4
|
+
export * from "./polygon.js";
|
|
5
|
+
export * from "./ray.js";
|
|
6
|
+
export * from "./segment.js";
|
|
7
|
+
export * from "./vec2.js";
|
|
8
|
+
export * from "./vec3.js";
|
|
9
|
+
export * from "./vec4.js";
|
|
10
|
+
export * from "./transformations.js";
|
|
11
|
+
export * from "./hollowgon.js";
|
|
12
|
+
export * from "./polyline.js";
|
|
13
|
+
export * from "./polygon-operations.js";
|
|
14
|
+
export * from "./ray3.js";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Vec2 } from "./vec2.js";
|
|
2
|
+
export declare class Line {
|
|
3
|
+
point: Vec2;
|
|
4
|
+
direction: Vec2;
|
|
5
|
+
constructor(point: Vec2, direction: Vec2);
|
|
6
|
+
static fromPoints(start: Vec2, end: Vec2): Line;
|
|
7
|
+
interpolate(time: number): Vec2;
|
|
8
|
+
computeIntersection(other: Line): Vec2 | null;
|
|
9
|
+
projectVectorOnto(vector: Vec2): Vec2;
|
|
10
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { EPS } from "../constants.js";
|
|
2
|
+
import { Vec2 } from "./vec2.js";
|
|
3
|
+
const { abs } = Math;
|
|
4
|
+
export class Line {
|
|
5
|
+
constructor(point, direction) {
|
|
6
|
+
this.point = point;
|
|
7
|
+
this.direction = direction;
|
|
8
|
+
}
|
|
9
|
+
static fromPoints(start, end) {
|
|
10
|
+
return new Line(start, Vec2.subtract(end, start));
|
|
11
|
+
}
|
|
12
|
+
interpolate(time) {
|
|
13
|
+
return Vec2.add(this.point, Vec2.multiply(this.direction, time));
|
|
14
|
+
}
|
|
15
|
+
computeIntersection(other) {
|
|
16
|
+
const denominator = this.direction.cross(other.direction);
|
|
17
|
+
if (abs(denominator) < EPS)
|
|
18
|
+
return null;
|
|
19
|
+
let time1 = Vec2.subtract(other.point, this.point).cross(other.direction) / denominator;
|
|
20
|
+
return this.interpolate(time1);
|
|
21
|
+
}
|
|
22
|
+
projectVectorOnto(vector) {
|
|
23
|
+
const originLineProjection = Vec2.subtract(vector, this.point).projectOnto(this.direction);
|
|
24
|
+
return Vec2.add(this.point, originLineProjection);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Hollowgon } from "./hollowgon.js";
|
|
2
|
+
import { Line } from "./line.js";
|
|
3
|
+
import { Polygon } from "./polygon.js";
|
|
4
|
+
import { Polyline } from "./polyline.js";
|
|
5
|
+
import { Ray } from "./ray.js";
|
|
6
|
+
import { Segment } from "./segment.js";
|
|
7
|
+
export type OperationShape = Polygon | Hollowgon;
|
|
8
|
+
export type SubdivisionObject = Line | Ray | Segment | Polyline;
|
|
9
|
+
type SubdivisionObjectInput<T extends OperationShape> = SubdivisionObject | ((shape: T) => SubdivisionObject);
|
|
10
|
+
export declare function subdivide(shape: Polygon | Array<Polygon>, subdivisionObject: SubdivisionObjectInput<Polygon>): Array<Polygon>;
|
|
11
|
+
export declare function subdivide(shape: Hollowgon | Array<Hollowgon>, subdivisionObject: SubdivisionObjectInput<Hollowgon>): Array<OperationShape>;
|
|
12
|
+
export declare function subdivide(shape: OperationShape | Array<OperationShape>, subdivisionObject: SubdivisionObjectInput<OperationShape>): Array<OperationShape>;
|
|
13
|
+
export declare function intersect(shapes: Array<Polygon>): Array<Polygon>;
|
|
14
|
+
export declare function intersect(shapes: Array<OperationShape>): Array<OperationShape>;
|
|
15
|
+
export declare function union(shapes: Array<Polygon>): Array<Polygon>;
|
|
16
|
+
export declare function union(shapes: Array<OperationShape>): Array<OperationShape>;
|
|
17
|
+
export declare function difference(shapes: Array<OperationShape>): OperationShape[];
|
|
18
|
+
export declare function xor(shapes: Array<OperationShape>): OperationShape[];
|
|
19
|
+
export declare function clip(shape: Polygon | Array<Polygon>, clipShapes: OperationShape | Array<OperationShape>): Array<Polygon>;
|
|
20
|
+
export declare function clip(shape: Hollowgon | Array<Hollowgon>, clipShapes: OperationShape | Array<OperationShape>): Array<OperationShape>;
|
|
21
|
+
export declare function clip(shape: OperationShape | Array<OperationShape>, clipShapes: OperationShape | Array<OperationShape>): Array<OperationShape>;
|
|
22
|
+
export declare const polygonOperations: {
|
|
23
|
+
subdivide: typeof subdivide;
|
|
24
|
+
intersect: typeof intersect;
|
|
25
|
+
union: typeof union;
|
|
26
|
+
difference: typeof difference;
|
|
27
|
+
xor: typeof xor;
|
|
28
|
+
clip: typeof clip;
|
|
29
|
+
};
|
|
30
|
+
export declare function createSubdivisionPolygon(shape: OperationShape, subdivisionObject: SubdivisionObject): Polygon | null;
|
|
31
|
+
export {};
|