pattapatta 0.3.0 → 0.4.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/CHANGELOG.md +6 -0
- package/dist/{circle-BDbKfrdR.d.ts → circle-CauDMv8V.d.ts} +1 -1
- package/dist/circlePacking/index.d.ts +2 -3
- package/dist/construction/index.d.ts +11 -3
- package/dist/construction/index.js +429 -2
- package/dist/construction/index.js.map +1 -1
- package/dist/contour/index.d.ts +3 -4
- package/dist/contour/index.js.map +1 -1
- package/dist/conversion/index.d.ts +2 -3
- package/dist/{group-BNjVVYSQ.d.ts → group-YVmSJuGo.d.ts} +1 -1
- package/dist/hatch/index.d.ts +70 -4
- package/dist/hatch/index.js +414 -3
- package/dist/hatch/index.js.map +1 -1
- package/dist/hull/index.d.ts +1 -2
- package/dist/index.d.ts +12 -13
- package/dist/index.js +1816 -1260
- package/dist/index.js.map +1 -1
- package/dist/meshing/index.d.ts +3 -4
- package/dist/morphology/index.d.ts +2 -3
- package/dist/optimisation/index.d.ts +2 -3
- package/dist/{path-0RjhYjJs.d.ts → path-Boeshk9L.d.ts} +9 -2
- package/dist/pointSet/index.d.ts +15 -2
- package/dist/pointSet/index.js +148 -2
- package/dist/pointSet/index.js.map +1 -1
- package/dist/polygonisation/index.d.ts +1 -2
- package/dist/polygonisation/index.js +54 -50
- package/dist/polygonisation/index.js.map +1 -1
- package/dist/predicates/index.d.ts +2 -3
- package/dist/processing/index.d.ts +3 -4
- package/dist/{segment-BKuIFMKD.d.ts → segment-CpLILSd7.d.ts} +1 -1
- package/dist/segmentSet/index.d.ts +41 -3
- package/dist/segmentSet/index.js +261 -1
- package/dist/segmentSet/index.js.map +1 -1
- package/dist/shapeBoolean/index.d.ts +2 -3
- package/dist/svg/index.d.ts +2 -3
- package/dist/tiling/index.d.ts +10 -4
- package/dist/tiling/index.js +148 -13
- package/dist/tiling/index.js.map +1 -1
- package/dist/transformation/index.d.ts +1 -2
- package/dist/triangulation/index.d.ts +1 -2
- package/dist/triangulation/index.js.map +1 -1
- package/dist/voronoi/index.d.ts +2 -3
- package/package.json +1 -1
- package/dist/vec2-CWyXN1Wj.d.ts +0 -10
package/dist/meshing/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { V as Vec2 } from '../
|
|
2
|
-
import { S as Segment } from '../segment-
|
|
3
|
-
import {
|
|
4
|
-
import { G as Group } from '../group-BNjVVYSQ.js';
|
|
1
|
+
import { P as Path, V as Vec2 } from '../path-Boeshk9L.js';
|
|
2
|
+
import { S as Segment } from '../segment-CpLILSd7.js';
|
|
3
|
+
import { G as Group } from '../group-YVmSJuGo.js';
|
|
5
4
|
|
|
6
5
|
/** Edges shared by at least two faces (excludes perimeter and hole boundaries). */
|
|
7
6
|
declare function extractInnerEdges(faces: Path[]): Segment[];
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { P as Path } from '../path-
|
|
2
|
-
import { G as Group } from '../group-
|
|
3
|
-
import '../vec2-CWyXN1Wj.js';
|
|
1
|
+
import { P as Path } from '../path-Boeshk9L.js';
|
|
2
|
+
import { G as Group } from '../group-YVmSJuGo.js';
|
|
4
3
|
|
|
5
4
|
type BufferOptions = {
|
|
6
5
|
/** Join style for offset. Default round. */
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { V as Vec2 } from '../
|
|
2
|
-
import { C as Circle } from '../circle-
|
|
3
|
-
import { P as Path } from '../path-0RjhYjJs.js';
|
|
1
|
+
import { P as Path, V as Vec2 } from '../path-Boeshk9L.js';
|
|
2
|
+
import { C as Circle } from '../circle-CauDMv8V.js';
|
|
4
3
|
|
|
5
4
|
/** Axis-aligned envelope (alias of hull.boundingBox). */
|
|
6
5
|
declare function envelope(p: Path): Path;
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
/** 2D point / vector. */
|
|
2
|
+
type Vec2 = {
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
};
|
|
6
|
+
declare function vec2(x: number, y: number): Vec2;
|
|
7
|
+
declare function cloneVec2(v: Vec2): Vec2;
|
|
8
|
+
declare function equalsVec2(a: Vec2, b: Vec2, eps?: number): boolean;
|
|
2
9
|
|
|
3
10
|
/** Ordered ring of vertices (typically without repeating the closing vertex). */
|
|
4
11
|
type Ring = Vec2[];
|
|
@@ -18,4 +25,4 @@ declare function polygon(exterior: Ring, holes?: Ring[]): Path;
|
|
|
18
25
|
declare function normalizeRing(ring: Ring, eps?: number): Ring;
|
|
19
26
|
declare function clonePath(p: Path): Path;
|
|
20
27
|
|
|
21
|
-
export { type Path as P, type Ring as R,
|
|
28
|
+
export { type Path as P, type Ring as R, type Vec2 as V, cloneVec2 as a, polygon as b, clonePath as c, polyline as d, equalsVec2 as e, normalizeRing as n, path as p, vec2 as v };
|
package/dist/pointSet/index.d.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import { V as Vec2 } from '../
|
|
1
|
+
import { V as Vec2, P as Path } from '../path-Boeshk9L.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Sort points by 2D Hilbert curve index (spatial locality).
|
|
5
|
+
* Returns a new array; does not mutate the input.
|
|
6
|
+
*/
|
|
7
|
+
declare function hilbertSort(points: Vec2[], order?: number): Vec2[];
|
|
2
8
|
|
|
3
9
|
/** Seeded uniform random points in an axis-aligned box. */
|
|
4
10
|
declare function random(count: number, minX: number, minY: number, maxX: number, maxY: number, seed?: number): Vec2[];
|
|
@@ -12,6 +18,11 @@ declare function ring(count: number, center: Vec2, radius: number): Vec2[];
|
|
|
12
18
|
declare function poisson(minDistance: number, minX: number, minY: number, maxX: number, maxY: number, seed?: number, maxAttempts?: number): Vec2[];
|
|
13
19
|
/** Drop points closer than `minDistance` (greedy keep-first). */
|
|
14
20
|
declare function prunePointsWithinDistance(points: Vec2[], minDistance: number): Vec2[];
|
|
21
|
+
/**
|
|
22
|
+
* Approximate shortest tour (TSP): nearest-neighbor constructive heuristic
|
|
23
|
+
* followed by 2-opt improvement. Returns a closed path visiting each point once.
|
|
24
|
+
*/
|
|
25
|
+
declare function findShortestTour(points: Vec2[]): Path;
|
|
15
26
|
declare const pointSet: {
|
|
16
27
|
random: typeof random;
|
|
17
28
|
squareGrid: typeof squareGrid;
|
|
@@ -19,6 +30,8 @@ declare const pointSet: {
|
|
|
19
30
|
ring: typeof ring;
|
|
20
31
|
poisson: typeof poisson;
|
|
21
32
|
prunePointsWithinDistance: typeof prunePointsWithinDistance;
|
|
33
|
+
hilbertSort: typeof hilbertSort;
|
|
34
|
+
findShortestTour: typeof findShortestTour;
|
|
22
35
|
};
|
|
23
36
|
|
|
24
|
-
export { hexGrid, pointSet, poisson, prunePointsWithinDistance, random, ring, squareGrid };
|
|
37
|
+
export { findShortestTour, hexGrid, hilbertSort, pointSet, poisson, prunePointsWithinDistance, random, ring, squareGrid };
|
package/dist/pointSet/index.js
CHANGED
|
@@ -1,3 +1,75 @@
|
|
|
1
|
+
// src/types/vec2.ts
|
|
2
|
+
function cloneVec2(v) {
|
|
3
|
+
return { x: v.x, y: v.y };
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
// src/types/path.ts
|
|
7
|
+
function path(rings, closed = true) {
|
|
8
|
+
return {
|
|
9
|
+
rings: rings.map((ring2) => ring2.map(cloneVec2)),
|
|
10
|
+
closed
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function polyline(points) {
|
|
14
|
+
return path([points], false);
|
|
15
|
+
}
|
|
16
|
+
function polygon(exterior, holes = []) {
|
|
17
|
+
return path([exterior, ...holes], true);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// src/pointSet/hilbert.ts
|
|
21
|
+
function xy2d(order, x, y) {
|
|
22
|
+
let rx;
|
|
23
|
+
let ry;
|
|
24
|
+
let s;
|
|
25
|
+
let d = 0;
|
|
26
|
+
for (s = 1 << order - 1; s > 0; s >>= 1) {
|
|
27
|
+
rx = (x & s) > 0 ? 1 : 0;
|
|
28
|
+
ry = (y & s) > 0 ? 1 : 0;
|
|
29
|
+
d += s * s * (3 * rx ^ ry);
|
|
30
|
+
({ x, y } = rot(s, x, y, rx, ry));
|
|
31
|
+
}
|
|
32
|
+
return d;
|
|
33
|
+
}
|
|
34
|
+
function rot(n, x, y, rx, ry) {
|
|
35
|
+
if (ry === 0) {
|
|
36
|
+
if (rx === 1) {
|
|
37
|
+
x = n - 1 - x;
|
|
38
|
+
y = n - 1 - y;
|
|
39
|
+
}
|
|
40
|
+
return { x: y, y: x };
|
|
41
|
+
}
|
|
42
|
+
return { x, y };
|
|
43
|
+
}
|
|
44
|
+
function hilbertSort(points, order = 10) {
|
|
45
|
+
if (points.length === 0) return [];
|
|
46
|
+
let minX = Infinity;
|
|
47
|
+
let minY = Infinity;
|
|
48
|
+
let maxX = -Infinity;
|
|
49
|
+
let maxY = -Infinity;
|
|
50
|
+
for (const p of points) {
|
|
51
|
+
minX = Math.min(minX, p.x);
|
|
52
|
+
minY = Math.min(minY, p.y);
|
|
53
|
+
maxX = Math.max(maxX, p.x);
|
|
54
|
+
maxY = Math.max(maxY, p.y);
|
|
55
|
+
}
|
|
56
|
+
const span = Math.max(maxX - minX, maxY - minY, 1e-9);
|
|
57
|
+
const n = 1 << order;
|
|
58
|
+
const scored = points.map((p) => {
|
|
59
|
+
const x = Math.min(
|
|
60
|
+
n - 1,
|
|
61
|
+
Math.max(0, Math.floor((p.x - minX) / span * (n - 1)))
|
|
62
|
+
);
|
|
63
|
+
const y = Math.min(
|
|
64
|
+
n - 1,
|
|
65
|
+
Math.max(0, Math.floor((p.y - minY) / span * (n - 1)))
|
|
66
|
+
);
|
|
67
|
+
return { p, h: xy2d(order, x, y) };
|
|
68
|
+
});
|
|
69
|
+
scored.sort((a, b) => a.h - b.h);
|
|
70
|
+
return scored.map((s) => s.p);
|
|
71
|
+
}
|
|
72
|
+
|
|
1
73
|
// src/pointSet/index.ts
|
|
2
74
|
function random(count, minX, minY, maxX, maxY, seed = 1) {
|
|
3
75
|
const rng = mulberry32(seed >>> 0);
|
|
@@ -125,15 +197,89 @@ function prunePointsWithinDistance(points, minDistance) {
|
|
|
125
197
|
}
|
|
126
198
|
return out;
|
|
127
199
|
}
|
|
200
|
+
function findShortestTour(points) {
|
|
201
|
+
if (points.length === 0) return polygon([]);
|
|
202
|
+
if (points.length === 1) return polygon([{ ...points[0] }]);
|
|
203
|
+
if (points.length === 2) {
|
|
204
|
+
return polyline([
|
|
205
|
+
{ ...points[0] },
|
|
206
|
+
{ ...points[1] },
|
|
207
|
+
{ ...points[0] }
|
|
208
|
+
]);
|
|
209
|
+
}
|
|
210
|
+
const tour = nearestNeighborTour(points);
|
|
211
|
+
twoOpt(tour);
|
|
212
|
+
return polygon(tour);
|
|
213
|
+
}
|
|
214
|
+
function nearestNeighborTour(points) {
|
|
215
|
+
const remaining = points.map((p) => ({ ...p }));
|
|
216
|
+
let startI = 0;
|
|
217
|
+
for (let i = 1; i < remaining.length; i++) {
|
|
218
|
+
const a = remaining[i];
|
|
219
|
+
const b = remaining[startI];
|
|
220
|
+
if (a.x < b.x || a.x === b.x && a.y < b.y) startI = i;
|
|
221
|
+
}
|
|
222
|
+
const ordered = [remaining.splice(startI, 1)[0]];
|
|
223
|
+
while (remaining.length) {
|
|
224
|
+
const last = ordered[ordered.length - 1];
|
|
225
|
+
let bestI = 0;
|
|
226
|
+
let bestD = Infinity;
|
|
227
|
+
for (let i = 0; i < remaining.length; i++) {
|
|
228
|
+
const d = Math.hypot(
|
|
229
|
+
remaining[i].x - last.x,
|
|
230
|
+
remaining[i].y - last.y
|
|
231
|
+
);
|
|
232
|
+
if (d < bestD) {
|
|
233
|
+
bestD = d;
|
|
234
|
+
bestI = i;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
ordered.push(remaining.splice(bestI, 1)[0]);
|
|
238
|
+
}
|
|
239
|
+
return ordered;
|
|
240
|
+
}
|
|
241
|
+
function twoOpt(tour) {
|
|
242
|
+
const n = tour.length;
|
|
243
|
+
if (n < 4) return;
|
|
244
|
+
let improved = true;
|
|
245
|
+
while (improved) {
|
|
246
|
+
improved = false;
|
|
247
|
+
for (let i = 0; i < n - 1; i++) {
|
|
248
|
+
for (let j = i + 2; j < n; j++) {
|
|
249
|
+
if (i === 0 && j === n - 1) continue;
|
|
250
|
+
const a = tour[i];
|
|
251
|
+
const b = tour[(i + 1) % n];
|
|
252
|
+
const c = tour[j];
|
|
253
|
+
const d = tour[(j + 1) % n];
|
|
254
|
+
const before = Math.hypot(a.x - b.x, a.y - b.y) + Math.hypot(c.x - d.x, c.y - d.y);
|
|
255
|
+
const after = Math.hypot(a.x - c.x, a.y - c.y) + Math.hypot(b.x - d.x, b.y - d.y);
|
|
256
|
+
if (after + 1e-12 < before) {
|
|
257
|
+
let lo = i + 1;
|
|
258
|
+
let hi = j;
|
|
259
|
+
while (lo < hi) {
|
|
260
|
+
const tmp = tour[lo];
|
|
261
|
+
tour[lo] = tour[hi];
|
|
262
|
+
tour[hi] = tmp;
|
|
263
|
+
lo++;
|
|
264
|
+
hi--;
|
|
265
|
+
}
|
|
266
|
+
improved = true;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
128
272
|
var pointSet = {
|
|
129
273
|
random,
|
|
130
274
|
squareGrid,
|
|
131
275
|
hexGrid,
|
|
132
276
|
ring,
|
|
133
277
|
poisson,
|
|
134
|
-
prunePointsWithinDistance
|
|
278
|
+
prunePointsWithinDistance,
|
|
279
|
+
hilbertSort,
|
|
280
|
+
findShortestTour
|
|
135
281
|
};
|
|
136
282
|
|
|
137
|
-
export { hexGrid, pointSet, poisson, prunePointsWithinDistance, random, ring, squareGrid };
|
|
283
|
+
export { findShortestTour, hexGrid, hilbertSort, pointSet, poisson, prunePointsWithinDistance, random, ring, squareGrid };
|
|
138
284
|
//# sourceMappingURL=index.js.map
|
|
139
285
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/pointSet/index.ts"],"names":[],"mappings":";AAGO,SAAS,OACd,KAAA,EACA,IAAA,EACA,MACA,IAAA,EACA,IAAA,EACA,OAAO,CAAA,EACC;AACR,EAAA,MAAM,GAAA,GAAM,UAAA,CAAW,IAAA,KAAS,CAAC,CAAA;AACjC,EAAA,MAAM,MAAc,EAAC;AACrB,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,EAAO,CAAA,EAAA,EAAK;AAC9B,IAAA,GAAA,CAAI,IAAA,CAAK;AAAA,MACP,CAAA,EAAG,IAAA,GAAO,GAAA,EAAI,IAAK,IAAA,GAAO,IAAA,CAAA;AAAA,MAC1B,CAAA,EAAG,IAAA,GAAO,GAAA,EAAI,IAAK,IAAA,GAAO,IAAA;AAAA,KAC3B,CAAA;AAAA,EACH;AACA,EAAA,OAAO,GAAA;AACT;AAGO,SAAS,UAAA,CACd,OAAA,EACA,IAAA,EACA,IAAA,EACA,MACA,IAAA,EACQ;AACR,EAAA,MAAM,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,OAAA,EAAS,IAAI,CAAA;AAChC,EAAA,MAAM,MAAc,EAAC;AACrB,EAAA,KAAA,IAAS,IAAI,IAAA,EAAM,CAAA,IAAK,IAAA,GAAO,KAAA,EAAO,KAAK,CAAA,EAAG;AAC5C,IAAA,KAAA,IAAS,IAAI,IAAA,EAAM,CAAA,IAAK,IAAA,GAAO,KAAA,EAAO,KAAK,CAAA,EAAG;AAC5C,MAAA,GAAA,CAAI,IAAA,CAAK,EAAE,CAAA,EAAG,CAAA,EAAG,CAAA;AAAA,IACnB;AAAA,EACF;AACA,EAAA,OAAO,GAAA;AACT;AAGO,SAAS,OAAA,CACd,OAAA,EACA,IAAA,EACA,IAAA,EACA,MACA,IAAA,EACQ;AACR,EAAA,MAAM,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,OAAA,EAAS,IAAI,CAAA;AAChC,EAAA,MAAM,EAAA,GAAK,CAAA;AACX,EAAA,MAAM,EAAA,GAAK,CAAA,GAAI,IAAA,CAAK,IAAA,CAAK,CAAC,CAAA,GAAI,GAAA;AAC9B,EAAA,MAAM,MAAc,EAAC;AACrB,EAAA,IAAI,GAAA,GAAM,CAAA;AACV,EAAA,KAAA,IAAS,IAAI,IAAA,EAAM,CAAA,IAAK,OAAO,KAAA,EAAO,CAAA,IAAK,IAAI,GAAA,EAAA,EAAO;AACpD,IAAA,MAAM,KAAK,IAAA,IAAQ,GAAA,GAAM,CAAA,KAAM,CAAA,GAAI,IAAI,CAAA,GAAI,GAAA,CAAA;AAC3C,IAAA,KAAA,IAAS,IAAI,EAAA,EAAI,CAAA,IAAK,IAAA,GAAO,KAAA,EAAO,KAAK,EAAA,EAAI;AAC3C,MAAA,GAAA,CAAI,IAAA,CAAK,EAAE,CAAA,EAAG,CAAA,EAAG,CAAA;AAAA,IACnB;AAAA,EACF;AACA,EAAA,OAAO,GAAA;AACT;AAGO,SAAS,IAAA,CACd,KAAA,EACA,MAAA,EACA,MAAA,EACQ;AACR,EAAA,MAAM,MAAc,EAAC;AACrB,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,EAAO,CAAA,EAAA,EAAK;AAC9B,IAAA,MAAM,CAAA,GAAK,CAAA,GAAI,KAAA,GAAS,IAAA,CAAK,EAAA,GAAK,CAAA;AAClC,IAAA,GAAA,CAAI,IAAA,CAAK;AAAA,MACP,GAAG,MAAA,CAAO,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,GAAI,MAAA;AAAA,MAC5B,GAAG,MAAA,CAAO,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,GAAI;AAAA,KAC7B,CAAA;AAAA,EACH;AACA,EAAA,OAAO,GAAA;AACT;AAEA,SAAS,WAAW,CAAA,EAAyB;AAC3C,EAAA,OAAO,WAAY;AACjB,IAAA,IAAI,IAAK,CAAA,IAAK,UAAA;AACd,IAAA,CAAA,GAAI,KAAK,IAAA,CAAK,CAAA,GAAK,CAAA,KAAM,EAAA,EAAK,IAAI,CAAC,CAAA;AACnC,IAAA,CAAA,IAAK,IAAI,IAAA,CAAK,IAAA,CAAK,IAAK,CAAA,KAAM,CAAA,EAAI,IAAI,EAAE,CAAA;AACxC,IAAA,OAAA,CAAA,CAAS,CAAA,GAAK,CAAA,KAAM,EAAA,MAAS,CAAA,IAAK,UAAA;AAAA,EACpC,CAAA;AACF;AAGO,SAAS,OAAA,CACd,aACA,IAAA,EACA,IAAA,EACA,MACA,IAAA,EACA,IAAA,GAAO,CAAA,EACP,WAAA,GAAc,EAAA,EACN;AACR,EAAA,MAAM,GAAA,GAAM,UAAA,CAAW,IAAA,KAAS,CAAC,CAAA;AACjC,EAAA,MAAM,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,WAAA,EAAa,IAAI,CAAA;AACpC,EAAA,MAAM,IAAA,GAAO,IAAI,IAAA,CAAK,KAAA;AACtB,EAAA,MAAM,IAAI,IAAA,GAAO,IAAA;AACjB,EAAA,MAAM,IAAI,IAAA,GAAO,IAAA;AACjB,EAAA,MAAM,EAAA,GAAK,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,IAAA,CAAK,CAAA,GAAI,IAAI,CAAC,CAAA;AAC1C,EAAA,MAAM,EAAA,GAAK,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,IAAA,CAAK,CAAA,GAAI,IAAI,CAAC,CAAA;AAC1C,EAAA,MAAM,OAA0B,KAAA,CAAM,EAAA,GAAK,EAAE,CAAA,CAAE,KAAK,IAAI,CAAA;AACxD,EAAA,MAAM,SAAiB,EAAC;AACxB,EAAA,MAAM,SAAmB,EAAC;AAE1B,EAAA,MAAM,EAAA,GAAK,CAAC,CAAA,KAAY,IAAA,CAAK,GAAA,CAAI,EAAA,GAAK,CAAA,EAAG,IAAA,CAAK,KAAA,CAAA,CAAO,CAAA,CAAE,CAAA,GAAI,IAAA,IAAQ,IAAI,CAAC,CAAA;AACxE,EAAA,MAAM,EAAA,GAAK,CAAC,CAAA,KAAY,IAAA,CAAK,GAAA,CAAI,EAAA,GAAK,CAAA,EAAG,IAAA,CAAK,KAAA,CAAA,CAAO,CAAA,CAAE,CAAA,GAAI,IAAA,IAAQ,IAAI,CAAC,CAAA;AAExE,EAAA,MAAM,IAAA,GAAO,CAAC,CAAA,KAAY;AACxB,IAAA,MAAM,IAAI,MAAA,CAAO,MAAA;AACjB,IAAA,MAAA,CAAO,KAAK,CAAC,CAAA;AACb,IAAA,MAAA,CAAO,KAAK,CAAC,CAAA;AACb,IAAA,IAAA,CAAK,GAAG,CAAC,CAAA,GAAI,KAAK,EAAA,CAAG,CAAC,CAAC,CAAA,GAAI,CAAA;AAAA,EAC7B,CAAA;AAEA,EAAA,IAAA,CAAK;AAAA,IACH,CAAA,EAAG,IAAA,GAAO,GAAA,EAAI,GAAI,CAAA;AAAA,IAClB,CAAA,EAAG,IAAA,GAAO,GAAA,EAAI,GAAI;AAAA,GACnB,CAAA;AAED,EAAA,OAAO,MAAA,CAAO,SAAS,CAAA,EAAG;AACxB,IAAA,MAAM,KAAK,IAAA,CAAK,KAAA,CAAM,GAAA,EAAI,GAAI,OAAO,MAAM,CAAA;AAC3C,IAAA,MAAM,CAAA,GAAI,OAAO,EAAE,CAAA;AACnB,IAAA,MAAM,GAAA,GAAM,OAAO,CAAC,CAAA;AACpB,IAAA,IAAI,KAAA,GAAQ,KAAA;AACZ,IAAA,KAAA,IAAS,OAAA,GAAU,CAAA,EAAG,OAAA,GAAU,WAAA,EAAa,OAAA,EAAA,EAAW;AACtD,MAAA,MAAM,GAAA,GAAM,GAAA,EAAI,GAAI,IAAA,CAAK,EAAA,GAAK,CAAA;AAC9B,MAAA,MAAM,GAAA,GAAM,CAAA,IAAK,CAAA,GAAI,GAAA,EAAI,CAAA;AACzB,MAAA,MAAM,IAAA,GAAO;AAAA,QACX,GAAG,GAAA,CAAI,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,GAAG,CAAA,GAAI,GAAA;AAAA,QAC3B,GAAG,GAAA,CAAI,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,GAAG,CAAA,GAAI;AAAA,OAC7B;AACA,MAAA,IACE,IAAA,CAAK,CAAA,GAAI,IAAA,IACT,IAAA,CAAK,CAAA,GAAI,IAAA,IACT,IAAA,CAAK,CAAA,GAAI,IAAA,IACT,IAAA,CAAK,CAAA,GAAI,IAAA,EACT;AACA,QAAA;AAAA,MACF;AACA,MAAA,MAAM,EAAA,GAAK,GAAG,IAAI,CAAA;AAClB,MAAA,MAAM,EAAA,GAAK,GAAG,IAAI,CAAA;AAClB,MAAA,IAAI,EAAA,GAAK,IAAA;AACT,MAAA,KAAA,IAAS,KAAK,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,EAAA,GAAK,CAAC,CAAA,EAAG,EAAA,IAAM,IAAA,CAAK,GAAA,CAAI,KAAK,CAAA,EAAG,EAAA,GAAK,CAAC,CAAA,IAAK,IAAI,EAAA,EAAA,EAAM;AAC7E,QAAA,KAAA,IAAS,EAAA,GAAK,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,KAAK,CAAC,CAAA,EAAG,EAAA,IAAM,IAAA,CAAK,IAAI,EAAA,GAAK,CAAA,EAAG,EAAA,GAAK,CAAC,GAAG,EAAA,EAAA,EAAM;AACvE,UAAA,MAAM,CAAA,GAAI,IAAA,CAAK,EAAA,GAAK,EAAA,GAAK,EAAE,CAAA;AAC3B,UAAA,IAAI,KAAK,IAAA,EAAM;AACf,UAAA,MAAM,CAAA,GAAI,OAAO,CAAC,CAAA;AAClB,UAAA,IAAI,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,CAAA,GAAI,CAAA,CAAE,CAAA,EAAG,IAAA,CAAK,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA,GAAI,CAAA,EAAG;AAC9C,YAAA,EAAA,GAAK,KAAA;AACL,YAAA;AAAA,UACF;AAAA,QACF;AAAA,MACF;AACA,MAAA,IAAI,EAAA,EAAI;AACN,QAAA,IAAA,CAAK,IAAI,CAAA;AACT,QAAA,KAAA,GAAQ,IAAA;AACR,QAAA;AAAA,MACF;AAAA,IACF;AACA,IAAA,IAAI,CAAC,KAAA,EAAO,MAAA,CAAO,MAAA,CAAO,IAAI,CAAC,CAAA;AAAA,EACjC;AACA,EAAA,OAAO,MAAA;AACT;AAGO,SAAS,yBAAA,CACd,QACA,WAAA,EACQ;AACR,EAAA,MAAM,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,WAAA,EAAa,CAAC,CAAA;AACjC,EAAA,MAAM,MAAc,EAAC;AACrB,EAAA,KAAA,MAAW,KAAK,MAAA,EAAQ;AACtB,IAAA,IAAI,IAAI,KAAA,CAAM,CAAC,CAAA,KAAM,IAAA,CAAK,MAAM,CAAA,CAAE,CAAA,GAAI,CAAA,CAAE,CAAA,EAAG,EAAE,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA,IAAK,CAAC,CAAA,EAAG;AAC3D,MAAA,GAAA,CAAI,KAAK,CAAC,CAAA;AAAA,IACZ;AAAA,EACF;AACA,EAAA,OAAO,GAAA;AACT;AAEO,IAAM,QAAA,GAAW;AAAA,EACtB,MAAA;AAAA,EACA,UAAA;AAAA,EACA,OAAA;AAAA,EACA,IAAA;AAAA,EACA,OAAA;AAAA,EACA;AACF","file":"index.js","sourcesContent":["import type { Vec2 } from '../types/index.js'\n\n/** Seeded uniform random points in an axis-aligned box. */\nexport function random(\n count: number,\n minX: number,\n minY: number,\n maxX: number,\n maxY: number,\n seed = 1,\n): Vec2[] {\n const rng = mulberry32(seed >>> 0)\n const out: Vec2[] = []\n for (let i = 0; i < count; i++) {\n out.push({\n x: minX + rng() * (maxX - minX),\n y: minY + rng() * (maxY - minY),\n })\n }\n return out\n}\n\n/** Square lattice points covering [minX,maxX] × [minY,maxY]. */\nexport function squareGrid(\n spacing: number,\n minX: number,\n minY: number,\n maxX: number,\n maxY: number,\n): Vec2[] {\n const s = Math.max(spacing, 1e-9)\n const out: Vec2[] = []\n for (let x = minX; x <= maxX + 1e-12; x += s) {\n for (let y = minY; y <= maxY + 1e-12; y += s) {\n out.push({ x, y })\n }\n }\n return out\n}\n\n/** Hexagonal lattice points covering the box. */\nexport function hexGrid(\n spacing: number,\n minX: number,\n minY: number,\n maxX: number,\n maxY: number,\n): Vec2[] {\n const s = Math.max(spacing, 1e-9)\n const dx = s\n const dy = s * Math.sqrt(3) * 0.5\n const out: Vec2[] = []\n let row = 0\n for (let y = minY; y <= maxY + 1e-12; y += dy, row++) {\n const x0 = minX + (row % 2 === 0 ? 0 : s * 0.5)\n for (let x = x0; x <= maxX + 1e-12; x += dx) {\n out.push({ x, y })\n }\n }\n return out\n}\n\n/** Ring of `count` points around `center` with `radius`. */\nexport function ring(\n count: number,\n center: Vec2,\n radius: number,\n): Vec2[] {\n const out: Vec2[] = []\n for (let i = 0; i < count; i++) {\n const t = (i / count) * Math.PI * 2\n out.push({\n x: center.x + Math.cos(t) * radius,\n y: center.y + Math.sin(t) * radius,\n })\n }\n return out\n}\n\nfunction mulberry32(a: number): () => number {\n return function () {\n let t = (a += 0x6d2b79f5)\n t = Math.imul(t ^ (t >>> 15), t | 1)\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61)\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296\n }\n}\n\n/** Bridson-ish Poisson disk sample in a box (seeded). */\nexport function poisson(\n minDistance: number,\n minX: number,\n minY: number,\n maxX: number,\n maxY: number,\n seed = 1,\n maxAttempts = 30,\n): Vec2[] {\n const rng = mulberry32(seed >>> 0)\n const r = Math.max(minDistance, 1e-9)\n const cell = r / Math.SQRT2\n const w = maxX - minX\n const h = maxY - minY\n const gw = Math.max(1, Math.ceil(w / cell))\n const gh = Math.max(1, Math.ceil(h / cell))\n const grid: (number | null)[] = Array(gw * gh).fill(null)\n const points: Vec2[] = []\n const active: number[] = []\n\n const gx = (p: Vec2) => Math.min(gw - 1, Math.floor((p.x - minX) / cell))\n const gy = (p: Vec2) => Math.min(gh - 1, Math.floor((p.y - minY) / cell))\n\n const emit = (p: Vec2) => {\n const i = points.length\n points.push(p)\n active.push(i)\n grid[gy(p) * gw + gx(p)] = i\n }\n\n emit({\n x: minX + rng() * w,\n y: minY + rng() * h,\n })\n\n while (active.length > 0) {\n const ai = Math.floor(rng() * active.length)\n const i = active[ai]!\n const src = points[i]!\n let found = false\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n const ang = rng() * Math.PI * 2\n const rad = r * (1 + rng())\n const cand = {\n x: src.x + Math.cos(ang) * rad,\n y: src.y + Math.sin(ang) * rad,\n }\n if (\n cand.x < minX ||\n cand.x > maxX ||\n cand.y < minY ||\n cand.y > maxY\n ) {\n continue\n }\n const cx = gx(cand)\n const cy = gy(cand)\n let ok = true\n for (let yy = Math.max(0, cy - 2); yy <= Math.min(gh - 1, cy + 2) && ok; yy++) {\n for (let xx = Math.max(0, cx - 2); xx <= Math.min(gw - 1, cx + 2); xx++) {\n const j = grid[yy * gw + xx]\n if (j == null) continue\n const q = points[j]!\n if (Math.hypot(cand.x - q.x, cand.y - q.y) < r) {\n ok = false\n break\n }\n }\n }\n if (ok) {\n emit(cand)\n found = true\n break\n }\n }\n if (!found) active.splice(ai, 1)\n }\n return points\n}\n\n/** Drop points closer than `minDistance` (greedy keep-first). */\nexport function prunePointsWithinDistance(\n points: Vec2[],\n minDistance: number,\n): Vec2[] {\n const r = Math.max(minDistance, 0)\n const out: Vec2[] = []\n for (const p of points) {\n if (out.every((q) => Math.hypot(p.x - q.x, p.y - q.y) >= r)) {\n out.push(p)\n }\n }\n return out\n}\n\nexport const pointSet = {\n random,\n squareGrid,\n hexGrid,\n ring,\n poisson,\n prunePointsWithinDistance,\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/types/vec2.ts","../../src/types/path.ts","../../src/pointSet/hilbert.ts","../../src/pointSet/index.ts"],"names":["ring"],"mappings":"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","file":"index.js","sourcesContent":["/** 2D point / vector. */\nexport type Vec2 = {\n x: number\n y: number\n}\n\nexport function vec2(x: number, y: number): Vec2 {\n return { x, y }\n}\n\nexport function cloneVec2(v: Vec2): Vec2 {\n return { x: v.x, y: v.y }\n}\n\nexport function equalsVec2(a: Vec2, b: Vec2, eps = 1e-9): boolean {\n return Math.abs(a.x - b.x) <= eps && Math.abs(a.y - b.y) <= eps\n}\n","import type { Vec2 } from './vec2.js'\nimport { cloneVec2, equalsVec2 } from './vec2.js'\n\n/** Ordered ring of vertices (typically without repeating the closing vertex). */\nexport type Ring = Vec2[]\n\n/**\n * A path: one exterior ring plus optional holes, or an open polyline.\n * `rings[0]` is the exterior (or the open polyline when `closed` is false).\n * Additional rings are holes (only meaningful when `closed` is true).\n */\nexport type Path = {\n rings: Ring[]\n closed: boolean\n}\n\nexport function path(rings: Ring[], closed = true): Path {\n return {\n rings: rings.map((ring) => ring.map(cloneVec2)),\n closed,\n }\n}\n\nexport function polyline(points: Ring): Path {\n return path([points], false)\n}\n\nexport function polygon(exterior: Ring, holes: Ring[] = []): Path {\n return path([exterior, ...holes], true)\n}\n\n/** Drop a trailing vertex that duplicates the first (SVG often closes this way). */\nexport function normalizeRing(ring: Ring, eps = 1e-9): Ring {\n if (ring.length < 2) return ring.map(cloneVec2)\n const first = ring[0]\n const last = ring[ring.length - 1]\n if (first && last && equalsVec2(first, last, eps)) {\n return ring.slice(0, -1).map(cloneVec2)\n }\n return ring.map(cloneVec2)\n}\n\nexport function clonePath(p: Path): Path {\n return {\n closed: p.closed,\n rings: p.rings.map((ring) => ring.map(cloneVec2)),\n }\n}\n","import type { Vec2 } from '../types/index.js'\n\n/** Hilbert index for (x,y) in [0, 2^order). */\nexport function xy2d(order: number, x: number, y: number): number {\n let rx\n let ry\n let s\n let d = 0\n for (s = 1 << (order - 1); s > 0; s >>= 1) {\n rx = (x & s) > 0 ? 1 : 0\n ry = (y & s) > 0 ? 1 : 0\n d += s * s * ((3 * rx) ^ ry)\n ;({ x, y } = rot(s, x, y, rx, ry))\n }\n return d\n}\n\nfunction rot(\n n: number,\n x: number,\n y: number,\n rx: number,\n ry: number,\n): { x: number; y: number } {\n if (ry === 0) {\n if (rx === 1) {\n x = n - 1 - x\n y = n - 1 - y\n }\n return { x: y, y: x }\n }\n return { x, y }\n}\n\n/**\n * Sort points by 2D Hilbert curve index (spatial locality).\n * Returns a new array; does not mutate the input.\n */\nexport function hilbertSort(points: Vec2[], order = 10): Vec2[] {\n if (points.length === 0) return []\n let minX = Infinity\n let minY = Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n for (const p of points) {\n minX = Math.min(minX, p.x)\n minY = Math.min(minY, p.y)\n maxX = Math.max(maxX, p.x)\n maxY = Math.max(maxY, p.y)\n }\n const span = Math.max(maxX - minX, maxY - minY, 1e-9)\n const n = 1 << order\n const scored = points.map((p) => {\n const x = Math.min(\n n - 1,\n Math.max(0, Math.floor(((p.x - minX) / span) * (n - 1))),\n )\n const y = Math.min(\n n - 1,\n Math.max(0, Math.floor(((p.y - minY) / span) * (n - 1))),\n )\n return { p, h: xy2d(order, x, y) }\n })\n scored.sort((a, b) => a.h - b.h)\n return scored.map((s) => s.p)\n}\n","import type { Path, Vec2 } from '../types/index.js'\nimport { polygon, polyline } from '../types/index.js'\nimport { hilbertSort } from './hilbert.js'\n\nexport { hilbertSort } from './hilbert.js'\n\n/** Seeded uniform random points in an axis-aligned box. */\nexport function random(\n count: number,\n minX: number,\n minY: number,\n maxX: number,\n maxY: number,\n seed = 1,\n): Vec2[] {\n const rng = mulberry32(seed >>> 0)\n const out: Vec2[] = []\n for (let i = 0; i < count; i++) {\n out.push({\n x: minX + rng() * (maxX - minX),\n y: minY + rng() * (maxY - minY),\n })\n }\n return out\n}\n\n/** Square lattice points covering [minX,maxX] × [minY,maxY]. */\nexport function squareGrid(\n spacing: number,\n minX: number,\n minY: number,\n maxX: number,\n maxY: number,\n): Vec2[] {\n const s = Math.max(spacing, 1e-9)\n const out: Vec2[] = []\n for (let x = minX; x <= maxX + 1e-12; x += s) {\n for (let y = minY; y <= maxY + 1e-12; y += s) {\n out.push({ x, y })\n }\n }\n return out\n}\n\n/** Hexagonal lattice points covering the box. */\nexport function hexGrid(\n spacing: number,\n minX: number,\n minY: number,\n maxX: number,\n maxY: number,\n): Vec2[] {\n const s = Math.max(spacing, 1e-9)\n const dx = s\n const dy = s * Math.sqrt(3) * 0.5\n const out: Vec2[] = []\n let row = 0\n for (let y = minY; y <= maxY + 1e-12; y += dy, row++) {\n const x0 = minX + (row % 2 === 0 ? 0 : s * 0.5)\n for (let x = x0; x <= maxX + 1e-12; x += dx) {\n out.push({ x, y })\n }\n }\n return out\n}\n\n/** Ring of `count` points around `center` with `radius`. */\nexport function ring(\n count: number,\n center: Vec2,\n radius: number,\n): Vec2[] {\n const out: Vec2[] = []\n for (let i = 0; i < count; i++) {\n const t = (i / count) * Math.PI * 2\n out.push({\n x: center.x + Math.cos(t) * radius,\n y: center.y + Math.sin(t) * radius,\n })\n }\n return out\n}\n\nfunction mulberry32(a: number): () => number {\n return function () {\n let t = (a += 0x6d2b79f5)\n t = Math.imul(t ^ (t >>> 15), t | 1)\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61)\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296\n }\n}\n\n/** Bridson-ish Poisson disk sample in a box (seeded). */\nexport function poisson(\n minDistance: number,\n minX: number,\n minY: number,\n maxX: number,\n maxY: number,\n seed = 1,\n maxAttempts = 30,\n): Vec2[] {\n const rng = mulberry32(seed >>> 0)\n const r = Math.max(minDistance, 1e-9)\n const cell = r / Math.SQRT2\n const w = maxX - minX\n const h = maxY - minY\n const gw = Math.max(1, Math.ceil(w / cell))\n const gh = Math.max(1, Math.ceil(h / cell))\n const grid: (number | null)[] = Array(gw * gh).fill(null)\n const points: Vec2[] = []\n const active: number[] = []\n\n const gx = (p: Vec2) => Math.min(gw - 1, Math.floor((p.x - minX) / cell))\n const gy = (p: Vec2) => Math.min(gh - 1, Math.floor((p.y - minY) / cell))\n\n const emit = (p: Vec2) => {\n const i = points.length\n points.push(p)\n active.push(i)\n grid[gy(p) * gw + gx(p)] = i\n }\n\n emit({\n x: minX + rng() * w,\n y: minY + rng() * h,\n })\n\n while (active.length > 0) {\n const ai = Math.floor(rng() * active.length)\n const i = active[ai]!\n const src = points[i]!\n let found = false\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n const ang = rng() * Math.PI * 2\n const rad = r * (1 + rng())\n const cand = {\n x: src.x + Math.cos(ang) * rad,\n y: src.y + Math.sin(ang) * rad,\n }\n if (\n cand.x < minX ||\n cand.x > maxX ||\n cand.y < minY ||\n cand.y > maxY\n ) {\n continue\n }\n const cx = gx(cand)\n const cy = gy(cand)\n let ok = true\n for (let yy = Math.max(0, cy - 2); yy <= Math.min(gh - 1, cy + 2) && ok; yy++) {\n for (let xx = Math.max(0, cx - 2); xx <= Math.min(gw - 1, cx + 2); xx++) {\n const j = grid[yy * gw + xx]\n if (j == null) continue\n const q = points[j]!\n if (Math.hypot(cand.x - q.x, cand.y - q.y) < r) {\n ok = false\n break\n }\n }\n }\n if (ok) {\n emit(cand)\n found = true\n break\n }\n }\n if (!found) active.splice(ai, 1)\n }\n return points\n}\n\n/** Drop points closer than `minDistance` (greedy keep-first). */\nexport function prunePointsWithinDistance(\n points: Vec2[],\n minDistance: number,\n): Vec2[] {\n const r = Math.max(minDistance, 0)\n const out: Vec2[] = []\n for (const p of points) {\n if (out.every((q) => Math.hypot(p.x - q.x, p.y - q.y) >= r)) {\n out.push(p)\n }\n }\n return out\n}\n\n/**\n * Approximate shortest tour (TSP): nearest-neighbor constructive heuristic\n * followed by 2-opt improvement. Returns a closed path visiting each point once.\n */\nexport function findShortestTour(points: Vec2[]): Path {\n if (points.length === 0) return polygon([])\n if (points.length === 1) return polygon([{ ...points[0]! }])\n if (points.length === 2) {\n return polyline([\n { ...points[0]! },\n { ...points[1]! },\n { ...points[0]! },\n ])\n }\n\n const tour = nearestNeighborTour(points)\n twoOpt(tour)\n return polygon(tour)\n}\n\nfunction nearestNeighborTour(points: Vec2[]): Vec2[] {\n const remaining = points.map((p) => ({ ...p }))\n // Start at lexicographically smallest point for determinism\n let startI = 0\n for (let i = 1; i < remaining.length; i++) {\n const a = remaining[i]!\n const b = remaining[startI]!\n if (a.x < b.x || (a.x === b.x && a.y < b.y)) startI = i\n }\n const ordered: Vec2[] = [remaining.splice(startI, 1)[0]!]\n while (remaining.length) {\n const last = ordered[ordered.length - 1]!\n let bestI = 0\n let bestD = Infinity\n for (let i = 0; i < remaining.length; i++) {\n const d = Math.hypot(\n remaining[i]!.x - last.x,\n remaining[i]!.y - last.y,\n )\n if (d < bestD) {\n bestD = d\n bestI = i\n }\n }\n ordered.push(remaining.splice(bestI, 1)[0]!)\n }\n return ordered\n}\n\n/** In-place 2-opt until no improving swap remains. */\nfunction twoOpt(tour: Vec2[]): void {\n const n = tour.length\n if (n < 4) return\n let improved = true\n while (improved) {\n improved = false\n for (let i = 0; i < n - 1; i++) {\n for (let j = i + 2; j < n; j++) {\n if (i === 0 && j === n - 1) continue\n const a = tour[i]!\n const b = tour[(i + 1) % n]!\n const c = tour[j]!\n const d = tour[(j + 1) % n]!\n const before =\n Math.hypot(a.x - b.x, a.y - b.y) + Math.hypot(c.x - d.x, c.y - d.y)\n const after =\n Math.hypot(a.x - c.x, a.y - c.y) + Math.hypot(b.x - d.x, b.y - d.y)\n if (after + 1e-12 < before) {\n // Reverse segment (i+1 .. j)\n let lo = i + 1\n let hi = j\n while (lo < hi) {\n const tmp = tour[lo]!\n tour[lo] = tour[hi]!\n tour[hi] = tmp\n lo++\n hi--\n }\n improved = true\n }\n }\n }\n }\n}\n\nexport const pointSet = {\n random,\n squareGrid,\n hexGrid,\n ring,\n poisson,\n prunePointsWithinDistance,\n hilbertSort,\n findShortestTour,\n}\n"]}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { V as Vec2 } from '../
|
|
2
|
-
import { P as Path } from '../path-0RjhYjJs.js';
|
|
1
|
+
import { V as Vec2, P as Path } from '../path-Boeshk9L.js';
|
|
3
2
|
|
|
4
3
|
/** Convex hull (largest-area simple polygonisation for a point cloud). */
|
|
5
4
|
declare function maxArea(points: Vec2[]): Path;
|
|
@@ -113,6 +113,59 @@ function uniquePoints(points) {
|
|
|
113
113
|
return out;
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
// src/pointSet/hilbert.ts
|
|
117
|
+
function xy2d(order, x, y) {
|
|
118
|
+
let rx;
|
|
119
|
+
let ry;
|
|
120
|
+
let s;
|
|
121
|
+
let d = 0;
|
|
122
|
+
for (s = 1 << order - 1; s > 0; s >>= 1) {
|
|
123
|
+
rx = (x & s) > 0 ? 1 : 0;
|
|
124
|
+
ry = (y & s) > 0 ? 1 : 0;
|
|
125
|
+
d += s * s * (3 * rx ^ ry);
|
|
126
|
+
({ x, y } = rot(s, x, y, rx, ry));
|
|
127
|
+
}
|
|
128
|
+
return d;
|
|
129
|
+
}
|
|
130
|
+
function rot(n, x, y, rx, ry) {
|
|
131
|
+
if (ry === 0) {
|
|
132
|
+
if (rx === 1) {
|
|
133
|
+
x = n - 1 - x;
|
|
134
|
+
y = n - 1 - y;
|
|
135
|
+
}
|
|
136
|
+
return { x: y, y: x };
|
|
137
|
+
}
|
|
138
|
+
return { x, y };
|
|
139
|
+
}
|
|
140
|
+
function hilbertSort(points, order = 10) {
|
|
141
|
+
if (points.length === 0) return [];
|
|
142
|
+
let minX = Infinity;
|
|
143
|
+
let minY = Infinity;
|
|
144
|
+
let maxX = -Infinity;
|
|
145
|
+
let maxY = -Infinity;
|
|
146
|
+
for (const p of points) {
|
|
147
|
+
minX = Math.min(minX, p.x);
|
|
148
|
+
minY = Math.min(minY, p.y);
|
|
149
|
+
maxX = Math.max(maxX, p.x);
|
|
150
|
+
maxY = Math.max(maxY, p.y);
|
|
151
|
+
}
|
|
152
|
+
const span = Math.max(maxX - minX, maxY - minY, 1e-9);
|
|
153
|
+
const n = 1 << order;
|
|
154
|
+
const scored = points.map((p) => {
|
|
155
|
+
const x = Math.min(
|
|
156
|
+
n - 1,
|
|
157
|
+
Math.max(0, Math.floor((p.x - minX) / span * (n - 1)))
|
|
158
|
+
);
|
|
159
|
+
const y = Math.min(
|
|
160
|
+
n - 1,
|
|
161
|
+
Math.max(0, Math.floor((p.y - minY) / span * (n - 1)))
|
|
162
|
+
);
|
|
163
|
+
return { p, h: xy2d(order, x, y) };
|
|
164
|
+
});
|
|
165
|
+
scored.sort((a, b) => a.h - b.h);
|
|
166
|
+
return scored.map((s) => s.p);
|
|
167
|
+
}
|
|
168
|
+
|
|
116
169
|
// src/polygonisation/index.ts
|
|
117
170
|
function maxArea(points) {
|
|
118
171
|
return convexHull(points);
|
|
@@ -204,33 +257,7 @@ function onionLayers(points) {
|
|
|
204
257
|
return layers;
|
|
205
258
|
}
|
|
206
259
|
function hilbert(points) {
|
|
207
|
-
|
|
208
|
-
let minX = Infinity;
|
|
209
|
-
let minY = Infinity;
|
|
210
|
-
let maxX = -Infinity;
|
|
211
|
-
let maxY = -Infinity;
|
|
212
|
-
for (const p of points) {
|
|
213
|
-
minX = Math.min(minX, p.x);
|
|
214
|
-
minY = Math.min(minY, p.y);
|
|
215
|
-
maxX = Math.max(maxX, p.x);
|
|
216
|
-
maxY = Math.max(maxY, p.y);
|
|
217
|
-
}
|
|
218
|
-
const span = Math.max(maxX - minX, maxY - minY, 1e-9);
|
|
219
|
-
const order = 10;
|
|
220
|
-
const n = 1 << order;
|
|
221
|
-
const scored = points.map((p) => {
|
|
222
|
-
const x = Math.min(
|
|
223
|
-
n - 1,
|
|
224
|
-
Math.max(0, Math.floor((p.x - minX) / span * (n - 1)))
|
|
225
|
-
);
|
|
226
|
-
const y = Math.min(
|
|
227
|
-
n - 1,
|
|
228
|
-
Math.max(0, Math.floor((p.y - minY) / span * (n - 1)))
|
|
229
|
-
);
|
|
230
|
-
return { p, h: xy2d(order, x, y) };
|
|
231
|
-
});
|
|
232
|
-
scored.sort((a, b) => a.h - b.h);
|
|
233
|
-
return polygon(scored.map((s) => s.p));
|
|
260
|
+
return polygon(hilbertSort(points));
|
|
234
261
|
}
|
|
235
262
|
function coversPoints(poly, points) {
|
|
236
263
|
const r = poly.rings[0];
|
|
@@ -269,29 +296,6 @@ function centroidOf(points) {
|
|
|
269
296
|
}
|
|
270
297
|
return { x: sx / points.length, y: sy / points.length };
|
|
271
298
|
}
|
|
272
|
-
function xy2d(order, x, y) {
|
|
273
|
-
let rx;
|
|
274
|
-
let ry;
|
|
275
|
-
let s;
|
|
276
|
-
let d = 0;
|
|
277
|
-
for (s = 1 << order - 1; s > 0; s >>= 1) {
|
|
278
|
-
rx = (x & s) > 0 ? 1 : 0;
|
|
279
|
-
ry = (y & s) > 0 ? 1 : 0;
|
|
280
|
-
d += s * s * (3 * rx ^ ry);
|
|
281
|
-
({ x, y } = rot(s, x, y, rx, ry));
|
|
282
|
-
}
|
|
283
|
-
return d;
|
|
284
|
-
}
|
|
285
|
-
function rot(n, x, y, rx, ry) {
|
|
286
|
-
if (ry === 0) {
|
|
287
|
-
if (rx === 1) {
|
|
288
|
-
x = n - 1 - x;
|
|
289
|
-
y = n - 1 - y;
|
|
290
|
-
}
|
|
291
|
-
return { x: y, y: x };
|
|
292
|
-
}
|
|
293
|
-
return { x, y };
|
|
294
|
-
}
|
|
295
299
|
var polygonisation = {
|
|
296
300
|
maxArea,
|
|
297
301
|
minArea,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/types/vec2.ts","../../src/types/path.ts","../../src/clipper/convert.ts","../../src/predicates/index.ts","../../src/hull/index.ts","../../src/polygonisation/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/** 2D point / vector. */\nexport type Vec2 = {\n x: number\n y: number\n}\n\nexport function vec2(x: number, y: number): Vec2 {\n return { x, y }\n}\n\nexport function cloneVec2(v: Vec2): Vec2 {\n return { x: v.x, y: v.y }\n}\n\nexport function equalsVec2(a: Vec2, b: Vec2, eps = 1e-9): boolean {\n return Math.abs(a.x - b.x) <= eps && Math.abs(a.y - b.y) <= eps\n}\n","import type { Vec2 } from './vec2.js'\nimport { cloneVec2, equalsVec2 } from './vec2.js'\n\n/** Ordered ring of vertices (typically without repeating the closing vertex). */\nexport type Ring = Vec2[]\n\n/**\n * A path: one exterior ring plus optional holes, or an open polyline.\n * `rings[0]` is the exterior (or the open polyline when `closed` is false).\n * Additional rings are holes (only meaningful when `closed` is true).\n */\nexport type Path = {\n rings: Ring[]\n closed: boolean\n}\n\nexport function path(rings: Ring[], closed = true): Path {\n return {\n rings: rings.map((ring) => ring.map(cloneVec2)),\n closed,\n }\n}\n\nexport function polyline(points: Ring): Path {\n return path([points], false)\n}\n\nexport function polygon(exterior: Ring, holes: Ring[] = []): Path {\n return path([exterior, ...holes], true)\n}\n\n/** Drop a trailing vertex that duplicates the first (SVG often closes this way). */\nexport function normalizeRing(ring: Ring, eps = 1e-9): Ring {\n if (ring.length < 2) return ring.map(cloneVec2)\n const first = ring[0]\n const last = ring[ring.length - 1]\n if (first && last && equalsVec2(first, last, eps)) {\n return ring.slice(0, -1).map(cloneVec2)\n }\n return ring.map(cloneVec2)\n}\n\nexport function clonePath(p: Path): Path {\n return {\n closed: p.closed,\n rings: p.rings.map((ring) => ring.map(cloneVec2)),\n }\n}\n","import {\n FillRule,\n areaD,\n differenceD,\n intersectD,\n isPositiveD,\n pointInPolygonD,\n PointInPolygonResult,\n unionD,\n xorD,\n type PathD,\n type PathsD,\n type PointD,\n} from 'clipper2-ts'\nimport type { Group, Path, Ring, Vec2 } from '../types/index.js'\nimport { group, normalizeRing, path, vec2 } from '../types/index.js'\n\nexport const DEFAULT_FILL_RULE = FillRule.NonZero\n\nexport function ringToPathD(ring: Ring): PathD {\n const pts: PointD[] = []\n for (const v of normalizeRing(ring)) {\n pts.push({ x: v.x, y: v.y, z: 0 })\n }\n return pts\n}\n\n/** Closed path → Clipper subject/clip paths (exterior + holes). */\nexport function pathToPathsD(p: Path): PathsD {\n if (!p.closed || p.rings.length === 0) {\n throw new Error('Boolean ops require a closed path with at least one ring')\n }\n return p.rings.map((ring) => ringToPathD(ring))\n}\n\nexport function pathsDToGroup(paths: PathsD): Group {\n if (paths.length === 0) return group([])\n\n const outers: { ring: Ring; holes: Ring[] }[] = []\n const holes: Ring[] = []\n\n for (const pd of paths) {\n const ring = pathDToRing(pd)\n if (ring.length < 3) continue\n if (isPositiveD(pd)) {\n outers.push({ ring, holes: [] })\n } else {\n holes.push(ring)\n }\n }\n\n for (const hole of holes) {\n const sample = hole[0]\n if (!sample) continue\n let assigned = false\n for (const outer of outers) {\n if (pointInRing(sample, outer.ring)) {\n outer.holes.push(hole)\n assigned = true\n break\n }\n }\n if (!assigned) {\n // Treat unmatched negative rings as their own outers (orientation flip).\n outers.push({ ring: [...hole].reverse(), holes: [] })\n }\n }\n\n return group(outers.map((o) => path([o.ring, ...o.holes], true)))\n}\n\nexport function pathDToRing(pd: PathD): Ring {\n return normalizeRing(pd.map((p) => vec2(p.x, p.y)))\n}\n\nexport function pointInRing(point: Vec2, ring: Ring): boolean {\n const result = pointInPolygonD(\n { x: point.x, y: point.y, z: 0 },\n ringToPathD(ring),\n )\n return (\n result === PointInPolygonResult.IsInside ||\n result === PointInPolygonResult.IsOn\n )\n}\n\nexport function clipUnion(a: PathsD, b: PathsD): PathsD {\n return unionD(a, b, DEFAULT_FILL_RULE)\n}\n\nexport function clipIntersect(a: PathsD, b: PathsD): PathsD {\n return intersectD(a, b, DEFAULT_FILL_RULE)\n}\n\nexport function clipDifference(a: PathsD, b: PathsD): PathsD {\n return differenceD(a, b, DEFAULT_FILL_RULE)\n}\n\nexport function clipXor(a: PathsD, b: PathsD): PathsD {\n return xorD(a, b, DEFAULT_FILL_RULE)\n}\n\nexport function pathsArea(paths: PathsD): number {\n let sum = 0\n for (const p of paths) {\n sum += Math.abs(areaD(p))\n }\n return sum\n}\n","import type { Group, Path, Vec2 } from '../types/index.js'\nimport { vec2 } from '../types/index.js'\nimport {\n pathToPathsD,\n pathsArea,\n pointInRing,\n} from '../clipper/convert.js'\n\n/** Signed area of a closed ring (shoelace). */\nexport function ringArea(ring: Vec2[]): number {\n if (ring.length < 3) return 0\n let sum = 0\n for (let i = 0; i < ring.length; i++) {\n const a = ring[i]!\n const b = ring[(i + 1) % ring.length]!\n sum += a.x * b.y - b.x * a.y\n }\n return sum / 2\n}\n\n/** Absolute area of a closed path (exterior minus holes). */\nexport function area(p: Path): number {\n if (!p.closed || p.rings.length === 0) return 0\n try {\n return pathsArea(pathToPathsD(p))\n } catch {\n let total = 0\n for (let i = 0; i < p.rings.length; i++) {\n const a = Math.abs(ringArea(p.rings[i]!))\n total += i === 0 ? a : -a\n }\n return Math.max(0, total)\n }\n}\n\nexport function areaGroup(g: Group): number {\n return g.paths.reduce((sum, p) => sum + area(p), 0)\n}\n\n/** Centroid of a closed exterior ring (ignores holes for Phase 2). */\nexport function centroid(p: Path): Vec2 {\n const ring = p.rings[0]\n if (!ring || ring.length === 0) return vec2(0, 0)\n const a = ringArea(ring)\n if (Math.abs(a) < 1e-12) {\n let sx = 0\n let sy = 0\n for (const v of ring) {\n sx += v.x\n sy += v.y\n }\n return vec2(sx / ring.length, sy / ring.length)\n }\n let cx = 0\n let cy = 0\n for (let i = 0; i < ring.length; i++) {\n const p0 = ring[i]!\n const p1 = ring[(i + 1) % ring.length]!\n const cross = p0.x * p1.y - p1.x * p0.y\n cx += (p0.x + p1.x) * cross\n cy += (p0.y + p1.y) * cross\n }\n return vec2(cx / (6 * a), cy / (6 * a))\n}\n\nexport function containsPoint(p: Path, point: Vec2): boolean {\n if (!p.closed || !p.rings[0]) return false\n if (!pointInRing(point, p.rings[0])) return false\n for (let i = 1; i < p.rings.length; i++) {\n const hole = p.rings[i]\n if (hole && pointInRing(point, hole)) return false\n }\n return true\n}\n\nexport function bounds(p: Path): {\n minX: number\n minY: number\n maxX: number\n maxY: number\n} {\n let minX = Infinity\n let minY = Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n for (const ring of p.rings) {\n for (const v of ring) {\n minX = Math.min(minX, v.x)\n minY = Math.min(minY, v.y)\n maxX = Math.max(maxX, v.x)\n maxY = Math.max(maxY, v.y)\n }\n }\n if (!Number.isFinite(minX)) {\n return { minX: 0, minY: 0, maxX: 0, maxY: 0 }\n }\n return { minX, minY, maxX, maxY }\n}\n\nexport function width(p: Path): number {\n const b = bounds(p)\n return b.maxX - b.minX\n}\n\nexport function height(p: Path): number {\n const b = bounds(p)\n return b.maxY - b.minY\n}\n\n/** Rough polygon similarity via absolute area difference relative to max area. */\nexport function areaSimilarity(a: Path, b: Path): number {\n const aa = area(a)\n const bb = area(b)\n const denom = Math.max(aa, bb, 1e-12)\n return 1 - Math.abs(aa - bb) / denom\n}\n\nexport const predicates = {\n area,\n areaGroup,\n ringArea,\n centroid,\n containsPoint,\n bounds,\n width,\n height,\n areaSimilarity,\n}\n","import type { Path, Vec2 } from '../types/index.js'\nimport { polygon } from '../types/index.js'\nimport { bounds } from '../predicates/index.js'\n\n/** Andrew's monotone-chain convex hull of a point set. */\nexport function convexHull(points: Vec2[]): Path {\n const pts = uniquePoints(points)\n if (pts.length < 3) return polygon(pts)\n\n pts.sort((a, b) => (a.x === b.x ? a.y - b.y : a.x - b.x))\n\n const lower: Vec2[] = []\n for (const p of pts) {\n while (\n lower.length >= 2 &&\n cross(lower[lower.length - 2]!, lower[lower.length - 1]!, p) <= 0\n ) {\n lower.pop()\n }\n lower.push(p)\n }\n\n const upper: Vec2[] = []\n for (let i = pts.length - 1; i >= 0; i--) {\n const p = pts[i]!\n while (\n upper.length >= 2 &&\n cross(upper[upper.length - 2]!, upper[upper.length - 1]!, p) <= 0\n ) {\n upper.pop()\n }\n upper.push(p)\n }\n\n lower.pop()\n upper.pop()\n return polygon([...lower, ...upper])\n}\n\n/** Convex hull of all vertices in a path (exterior + holes). */\nexport function convexHullPath(p: Path): Path {\n const pts: Vec2[] = []\n for (const ring of p.rings) pts.push(...ring)\n return convexHull(pts)\n}\n\n/** Axis-aligned bounding box of a path as a closed rectangle. */\nexport function boundingBox(p: Path): Path {\n const b = bounds(p)\n return polygon([\n { x: b.minX, y: b.minY },\n { x: b.maxX, y: b.minY },\n { x: b.maxX, y: b.maxY },\n { x: b.minX, y: b.maxY },\n ])\n}\n\nfunction cross(o: Vec2, a: Vec2, b: Vec2): number {\n return (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x)\n}\n\nfunction uniquePoints(points: Vec2[]): Vec2[] {\n const seen = new Set<string>()\n const out: Vec2[] = []\n for (const p of points) {\n const key = `${p.x},${p.y}`\n if (seen.has(key)) continue\n seen.add(key)\n out.push({ x: p.x, y: p.y })\n }\n return out\n}\n\nexport const hull = {\n convexHull,\n convexHullPath,\n boundingBox,\n}\n","import type { Path, Vec2 } from '../types/index.js'\nimport { polygon } from '../types/index.js'\nimport { convexHull } from '../hull/index.js'\nimport { area } from '../predicates/index.js'\n\n/** Convex hull (largest-area simple polygonisation for a point cloud). */\nexport function maxArea(points: Vec2[]): Path {\n return convexHull(points)\n}\n\n/**\n * Minimum-area triangle covering the points among hull edges\n * (O(h²) rotating-calipers style brute force on hull).\n * Falls back to hull if fewer than 3 hull vertices.\n */\nexport function minArea(points: Vec2[]): Path {\n const hull = convexHull(points).rings[0] ?? []\n if (hull.length < 3) return convexHull(points)\n if (hull.length === 3) return polygon(hull)\n\n let best: Path | null = null\n let bestA = Infinity\n const h = hull\n const n = h.length\n for (let i = 0; i < n; i++) {\n for (let j = i + 1; j < n; j++) {\n for (let k = j + 1; k < n; k++) {\n const tri = polygon([h[i]!, h[j]!, h[k]!])\n if (!coversPoints(tri, points)) continue\n const a = area(tri)\n if (a < bestA) {\n bestA = a\n best = tri\n }\n }\n }\n }\n return best ?? convexHull(points)\n}\n\n/** Closed polygon connecting points in order of minimum spanning path heuristic (NN tour). */\nexport function minPerimeter(points: Vec2[]): Path {\n if (points.length === 0) return polygon([])\n if (points.length < 3) return polygon(points)\n const remaining = points.map((p) => ({ ...p }))\n const ordered: Vec2[] = [remaining.shift()!]\n while (remaining.length) {\n const last = ordered[ordered.length - 1]!\n let bestI = 0\n let bestD = Infinity\n for (let i = 0; i < remaining.length; i++) {\n const d = Math.hypot(\n remaining[i]!.x - last.x,\n remaining[i]!.y - last.y,\n )\n if (d < bestD) {\n bestD = d\n bestI = i\n }\n }\n ordered.push(remaining.splice(bestI, 1)[0]!)\n }\n return polygon(ordered)\n}\n\n/** Connect points sorted by x then y. */\nexport function horizontal(points: Vec2[]): Path {\n const pts = [...points].sort((a, b) =>\n a.y === b.y ? a.x - b.x : a.y - b.y,\n )\n return polygon(pts)\n}\n\n/** Connect points sorted by y then x. */\nexport function vertical(points: Vec2[]): Path {\n const pts = [...points].sort((a, b) =>\n a.x === b.x ? a.y - b.y : a.x - b.x,\n )\n return polygon(pts)\n}\n\n/** Connect by polar angle around centroid. */\nexport function angular(points: Vec2[]): Path {\n if (points.length === 0) return polygon([])\n const c = centroidOf(points)\n const pts = [...points].sort(\n (a, b) =>\n Math.atan2(a.y - c.y, a.x - c.x) - Math.atan2(b.y - c.y, b.x - c.x),\n )\n return polygon(pts)\n}\n\n/** Alias of angular. */\nexport function circular(points: Vec2[]): Path {\n return angular(points)\n}\n\n/**\n * Onion layers: successive convex hulls. Returns the outermost hull polygon;\n * use `onionLayers` for all peels.\n */\nexport function onion(points: Vec2[]): Path {\n const layers = onionLayers(points)\n return layers[0] ?? polygon([])\n}\n\n/** Convex onion peels from outside in. */\nexport function onionLayers(points: Vec2[]): Path[] {\n let remaining = points.map((p) => ({ ...p }))\n const layers: Path[] = []\n while (remaining.length >= 3) {\n const hull = convexHull(remaining)\n const ring = hull.rings[0] ?? []\n if (ring.length < 3) break\n layers.push(hull)\n const onHull = new Set(ring.map((v) => `${v.x},${v.y}`))\n remaining = remaining.filter((p) => !onHull.has(`${p.x},${p.y}`))\n }\n return layers\n}\n\n/** Connect points ordered by 2D Hilbert curve index. */\nexport function hilbert(points: Vec2[]): Path {\n if (points.length === 0) return polygon([])\n let minX = Infinity\n let minY = Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n for (const p of points) {\n minX = Math.min(minX, p.x)\n minY = Math.min(minY, p.y)\n maxX = Math.max(maxX, p.x)\n maxY = Math.max(maxY, p.y)\n }\n const span = Math.max(maxX - minX, maxY - minY, 1e-9)\n const order = 10\n const n = 1 << order\n const scored = points.map((p) => {\n const x = Math.min(\n n - 1,\n Math.max(0, Math.floor(((p.x - minX) / span) * (n - 1))),\n )\n const y = Math.min(\n n - 1,\n Math.max(0, Math.floor(((p.y - minY) / span) * (n - 1))),\n )\n return { p, h: xy2d(order, x, y) }\n })\n scored.sort((a, b) => a.h - b.h)\n return polygon(scored.map((s) => s.p))\n}\n\nfunction coversPoints(poly: Path, points: Vec2[]): boolean {\n // triangle cover check via barycentric\n const r = poly.rings[0]\n if (!r || r.length < 3) return false\n const a = r[0]!\n const b = r[1]!\n const c = r[2]!\n for (const p of points) {\n if (!pointInTriangle(p, a, b, c)) return false\n }\n return true\n}\n\nfunction pointInTriangle(p: Vec2, a: Vec2, b: Vec2, c: Vec2): boolean {\n const v0x = c.x - a.x\n const v0y = c.y - a.y\n const v1x = b.x - a.x\n const v1y = b.y - a.y\n const v2x = p.x - a.x\n const v2y = p.y - a.y\n const dot00 = v0x * v0x + v0y * v0y\n const dot01 = v0x * v1x + v0y * v1y\n const dot02 = v0x * v2x + v0y * v2y\n const dot11 = v1x * v1x + v1y * v1y\n const dot12 = v1x * v2x + v1y * v2y\n const inv = 1 / (dot00 * dot11 - dot01 * dot01 || 1e-18)\n const u = (dot11 * dot02 - dot01 * dot12) * inv\n const v = (dot00 * dot12 - dot01 * dot02) * inv\n return u >= -1e-9 && v >= -1e-9 && u + v <= 1 + 1e-9\n}\n\nfunction centroidOf(points: Vec2[]): Vec2 {\n let sx = 0\n let sy = 0\n for (const p of points) {\n sx += p.x\n sy += p.y\n }\n return { x: sx / points.length, y: sy / points.length }\n}\n\n/** Hilbert index for (x,y) in [0, 2^order). */\nfunction xy2d(order: number, x: number, y: number): number {\n let rx\n let ry\n let s\n let d = 0\n for (s = 1 << (order - 1); s > 0; s >>= 1) {\n rx = (x & s) > 0 ? 1 : 0\n ry = (y & s) > 0 ? 1 : 0\n d += s * s * ((3 * rx) ^ ry)\n ;({ x, y } = rot(s, x, y, rx, ry))\n }\n return d\n}\n\nfunction rot(\n n: number,\n x: number,\n y: number,\n rx: number,\n ry: number,\n): { x: number; y: number } {\n if (ry === 0) {\n if (rx === 1) {\n x = n - 1 - x\n y = n - 1 - y\n }\n return { x: y, y: x }\n }\n return { x, y }\n}\n\nexport const polygonisation = {\n maxArea,\n minArea,\n minPerimeter,\n horizontal,\n vertical,\n angular,\n circular,\n onion,\n onionLayers,\n hilbert,\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/types/vec2.ts","../../src/types/path.ts","../../src/clipper/convert.ts","../../src/predicates/index.ts","../../src/hull/index.ts","../../src/pointSet/hilbert.ts","../../src/polygonisation/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/** 2D point / vector. */\nexport type Vec2 = {\n x: number\n y: number\n}\n\nexport function vec2(x: number, y: number): Vec2 {\n return { x, y }\n}\n\nexport function cloneVec2(v: Vec2): Vec2 {\n return { x: v.x, y: v.y }\n}\n\nexport function equalsVec2(a: Vec2, b: Vec2, eps = 1e-9): boolean {\n return Math.abs(a.x - b.x) <= eps && Math.abs(a.y - b.y) <= eps\n}\n","import type { Vec2 } from './vec2.js'\nimport { cloneVec2, equalsVec2 } from './vec2.js'\n\n/** Ordered ring of vertices (typically without repeating the closing vertex). */\nexport type Ring = Vec2[]\n\n/**\n * A path: one exterior ring plus optional holes, or an open polyline.\n * `rings[0]` is the exterior (or the open polyline when `closed` is false).\n * Additional rings are holes (only meaningful when `closed` is true).\n */\nexport type Path = {\n rings: Ring[]\n closed: boolean\n}\n\nexport function path(rings: Ring[], closed = true): Path {\n return {\n rings: rings.map((ring) => ring.map(cloneVec2)),\n closed,\n }\n}\n\nexport function polyline(points: Ring): Path {\n return path([points], false)\n}\n\nexport function polygon(exterior: Ring, holes: Ring[] = []): Path {\n return path([exterior, ...holes], true)\n}\n\n/** Drop a trailing vertex that duplicates the first (SVG often closes this way). */\nexport function normalizeRing(ring: Ring, eps = 1e-9): Ring {\n if (ring.length < 2) return ring.map(cloneVec2)\n const first = ring[0]\n const last = ring[ring.length - 1]\n if (first && last && equalsVec2(first, last, eps)) {\n return ring.slice(0, -1).map(cloneVec2)\n }\n return ring.map(cloneVec2)\n}\n\nexport function clonePath(p: Path): Path {\n return {\n closed: p.closed,\n rings: p.rings.map((ring) => ring.map(cloneVec2)),\n }\n}\n","import {\n FillRule,\n areaD,\n differenceD,\n intersectD,\n isPositiveD,\n pointInPolygonD,\n PointInPolygonResult,\n unionD,\n xorD,\n type PathD,\n type PathsD,\n type PointD,\n} from 'clipper2-ts'\nimport type { Group, Path, Ring, Vec2 } from '../types/index.js'\nimport { group, normalizeRing, path, vec2 } from '../types/index.js'\n\nexport const DEFAULT_FILL_RULE = FillRule.NonZero\n\nexport function ringToPathD(ring: Ring): PathD {\n const pts: PointD[] = []\n for (const v of normalizeRing(ring)) {\n pts.push({ x: v.x, y: v.y, z: 0 })\n }\n return pts\n}\n\n/** Closed path → Clipper subject/clip paths (exterior + holes). */\nexport function pathToPathsD(p: Path): PathsD {\n if (!p.closed || p.rings.length === 0) {\n throw new Error('Boolean ops require a closed path with at least one ring')\n }\n return p.rings.map((ring) => ringToPathD(ring))\n}\n\nexport function pathsDToGroup(paths: PathsD): Group {\n if (paths.length === 0) return group([])\n\n const outers: { ring: Ring; holes: Ring[] }[] = []\n const holes: Ring[] = []\n\n for (const pd of paths) {\n const ring = pathDToRing(pd)\n if (ring.length < 3) continue\n if (isPositiveD(pd)) {\n outers.push({ ring, holes: [] })\n } else {\n holes.push(ring)\n }\n }\n\n for (const hole of holes) {\n const sample = hole[0]\n if (!sample) continue\n let assigned = false\n for (const outer of outers) {\n if (pointInRing(sample, outer.ring)) {\n outer.holes.push(hole)\n assigned = true\n break\n }\n }\n if (!assigned) {\n // Treat unmatched negative rings as their own outers (orientation flip).\n outers.push({ ring: [...hole].reverse(), holes: [] })\n }\n }\n\n return group(outers.map((o) => path([o.ring, ...o.holes], true)))\n}\n\nexport function pathDToRing(pd: PathD): Ring {\n return normalizeRing(pd.map((p) => vec2(p.x, p.y)))\n}\n\nexport function pointInRing(point: Vec2, ring: Ring): boolean {\n const result = pointInPolygonD(\n { x: point.x, y: point.y, z: 0 },\n ringToPathD(ring),\n )\n return (\n result === PointInPolygonResult.IsInside ||\n result === PointInPolygonResult.IsOn\n )\n}\n\nexport function clipUnion(a: PathsD, b: PathsD): PathsD {\n return unionD(a, b, DEFAULT_FILL_RULE)\n}\n\nexport function clipIntersect(a: PathsD, b: PathsD): PathsD {\n return intersectD(a, b, DEFAULT_FILL_RULE)\n}\n\nexport function clipDifference(a: PathsD, b: PathsD): PathsD {\n return differenceD(a, b, DEFAULT_FILL_RULE)\n}\n\nexport function clipXor(a: PathsD, b: PathsD): PathsD {\n return xorD(a, b, DEFAULT_FILL_RULE)\n}\n\nexport function pathsArea(paths: PathsD): number {\n let sum = 0\n for (const p of paths) {\n sum += Math.abs(areaD(p))\n }\n return sum\n}\n","import type { Group, Path, Vec2 } from '../types/index.js'\nimport { vec2 } from '../types/index.js'\nimport {\n pathToPathsD,\n pathsArea,\n pointInRing,\n} from '../clipper/convert.js'\n\n/** Signed area of a closed ring (shoelace). */\nexport function ringArea(ring: Vec2[]): number {\n if (ring.length < 3) return 0\n let sum = 0\n for (let i = 0; i < ring.length; i++) {\n const a = ring[i]!\n const b = ring[(i + 1) % ring.length]!\n sum += a.x * b.y - b.x * a.y\n }\n return sum / 2\n}\n\n/** Absolute area of a closed path (exterior minus holes). */\nexport function area(p: Path): number {\n if (!p.closed || p.rings.length === 0) return 0\n try {\n return pathsArea(pathToPathsD(p))\n } catch {\n let total = 0\n for (let i = 0; i < p.rings.length; i++) {\n const a = Math.abs(ringArea(p.rings[i]!))\n total += i === 0 ? a : -a\n }\n return Math.max(0, total)\n }\n}\n\nexport function areaGroup(g: Group): number {\n return g.paths.reduce((sum, p) => sum + area(p), 0)\n}\n\n/** Centroid of a closed exterior ring (ignores holes for Phase 2). */\nexport function centroid(p: Path): Vec2 {\n const ring = p.rings[0]\n if (!ring || ring.length === 0) return vec2(0, 0)\n const a = ringArea(ring)\n if (Math.abs(a) < 1e-12) {\n let sx = 0\n let sy = 0\n for (const v of ring) {\n sx += v.x\n sy += v.y\n }\n return vec2(sx / ring.length, sy / ring.length)\n }\n let cx = 0\n let cy = 0\n for (let i = 0; i < ring.length; i++) {\n const p0 = ring[i]!\n const p1 = ring[(i + 1) % ring.length]!\n const cross = p0.x * p1.y - p1.x * p0.y\n cx += (p0.x + p1.x) * cross\n cy += (p0.y + p1.y) * cross\n }\n return vec2(cx / (6 * a), cy / (6 * a))\n}\n\nexport function containsPoint(p: Path, point: Vec2): boolean {\n if (!p.closed || !p.rings[0]) return false\n if (!pointInRing(point, p.rings[0])) return false\n for (let i = 1; i < p.rings.length; i++) {\n const hole = p.rings[i]\n if (hole && pointInRing(point, hole)) return false\n }\n return true\n}\n\nexport function bounds(p: Path): {\n minX: number\n minY: number\n maxX: number\n maxY: number\n} {\n let minX = Infinity\n let minY = Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n for (const ring of p.rings) {\n for (const v of ring) {\n minX = Math.min(minX, v.x)\n minY = Math.min(minY, v.y)\n maxX = Math.max(maxX, v.x)\n maxY = Math.max(maxY, v.y)\n }\n }\n if (!Number.isFinite(minX)) {\n return { minX: 0, minY: 0, maxX: 0, maxY: 0 }\n }\n return { minX, minY, maxX, maxY }\n}\n\nexport function width(p: Path): number {\n const b = bounds(p)\n return b.maxX - b.minX\n}\n\nexport function height(p: Path): number {\n const b = bounds(p)\n return b.maxY - b.minY\n}\n\n/** Rough polygon similarity via absolute area difference relative to max area. */\nexport function areaSimilarity(a: Path, b: Path): number {\n const aa = area(a)\n const bb = area(b)\n const denom = Math.max(aa, bb, 1e-12)\n return 1 - Math.abs(aa - bb) / denom\n}\n\nexport const predicates = {\n area,\n areaGroup,\n ringArea,\n centroid,\n containsPoint,\n bounds,\n width,\n height,\n areaSimilarity,\n}\n","import type { Path, Vec2 } from '../types/index.js'\nimport { polygon } from '../types/index.js'\nimport { bounds } from '../predicates/index.js'\n\n/** Andrew's monotone-chain convex hull of a point set. */\nexport function convexHull(points: Vec2[]): Path {\n const pts = uniquePoints(points)\n if (pts.length < 3) return polygon(pts)\n\n pts.sort((a, b) => (a.x === b.x ? a.y - b.y : a.x - b.x))\n\n const lower: Vec2[] = []\n for (const p of pts) {\n while (\n lower.length >= 2 &&\n cross(lower[lower.length - 2]!, lower[lower.length - 1]!, p) <= 0\n ) {\n lower.pop()\n }\n lower.push(p)\n }\n\n const upper: Vec2[] = []\n for (let i = pts.length - 1; i >= 0; i--) {\n const p = pts[i]!\n while (\n upper.length >= 2 &&\n cross(upper[upper.length - 2]!, upper[upper.length - 1]!, p) <= 0\n ) {\n upper.pop()\n }\n upper.push(p)\n }\n\n lower.pop()\n upper.pop()\n return polygon([...lower, ...upper])\n}\n\n/** Convex hull of all vertices in a path (exterior + holes). */\nexport function convexHullPath(p: Path): Path {\n const pts: Vec2[] = []\n for (const ring of p.rings) pts.push(...ring)\n return convexHull(pts)\n}\n\n/** Axis-aligned bounding box of a path as a closed rectangle. */\nexport function boundingBox(p: Path): Path {\n const b = bounds(p)\n return polygon([\n { x: b.minX, y: b.minY },\n { x: b.maxX, y: b.minY },\n { x: b.maxX, y: b.maxY },\n { x: b.minX, y: b.maxY },\n ])\n}\n\nfunction cross(o: Vec2, a: Vec2, b: Vec2): number {\n return (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x)\n}\n\nfunction uniquePoints(points: Vec2[]): Vec2[] {\n const seen = new Set<string>()\n const out: Vec2[] = []\n for (const p of points) {\n const key = `${p.x},${p.y}`\n if (seen.has(key)) continue\n seen.add(key)\n out.push({ x: p.x, y: p.y })\n }\n return out\n}\n\nexport const hull = {\n convexHull,\n convexHullPath,\n boundingBox,\n}\n","import type { Vec2 } from '../types/index.js'\n\n/** Hilbert index for (x,y) in [0, 2^order). */\nexport function xy2d(order: number, x: number, y: number): number {\n let rx\n let ry\n let s\n let d = 0\n for (s = 1 << (order - 1); s > 0; s >>= 1) {\n rx = (x & s) > 0 ? 1 : 0\n ry = (y & s) > 0 ? 1 : 0\n d += s * s * ((3 * rx) ^ ry)\n ;({ x, y } = rot(s, x, y, rx, ry))\n }\n return d\n}\n\nfunction rot(\n n: number,\n x: number,\n y: number,\n rx: number,\n ry: number,\n): { x: number; y: number } {\n if (ry === 0) {\n if (rx === 1) {\n x = n - 1 - x\n y = n - 1 - y\n }\n return { x: y, y: x }\n }\n return { x, y }\n}\n\n/**\n * Sort points by 2D Hilbert curve index (spatial locality).\n * Returns a new array; does not mutate the input.\n */\nexport function hilbertSort(points: Vec2[], order = 10): Vec2[] {\n if (points.length === 0) return []\n let minX = Infinity\n let minY = Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n for (const p of points) {\n minX = Math.min(minX, p.x)\n minY = Math.min(minY, p.y)\n maxX = Math.max(maxX, p.x)\n maxY = Math.max(maxY, p.y)\n }\n const span = Math.max(maxX - minX, maxY - minY, 1e-9)\n const n = 1 << order\n const scored = points.map((p) => {\n const x = Math.min(\n n - 1,\n Math.max(0, Math.floor(((p.x - minX) / span) * (n - 1))),\n )\n const y = Math.min(\n n - 1,\n Math.max(0, Math.floor(((p.y - minY) / span) * (n - 1))),\n )\n return { p, h: xy2d(order, x, y) }\n })\n scored.sort((a, b) => a.h - b.h)\n return scored.map((s) => s.p)\n}\n","import type { Path, Vec2 } from '../types/index.js'\nimport { polygon } from '../types/index.js'\nimport { convexHull } from '../hull/index.js'\nimport { area } from '../predicates/index.js'\nimport { hilbertSort } from '../pointSet/hilbert.js'\n\n/** Convex hull (largest-area simple polygonisation for a point cloud). */\nexport function maxArea(points: Vec2[]): Path {\n return convexHull(points)\n}\n\n/**\n * Minimum-area triangle covering the points among hull edges\n * (O(h²) rotating-calipers style brute force on hull).\n * Falls back to hull if fewer than 3 hull vertices.\n */\nexport function minArea(points: Vec2[]): Path {\n const hull = convexHull(points).rings[0] ?? []\n if (hull.length < 3) return convexHull(points)\n if (hull.length === 3) return polygon(hull)\n\n let best: Path | null = null\n let bestA = Infinity\n const h = hull\n const n = h.length\n for (let i = 0; i < n; i++) {\n for (let j = i + 1; j < n; j++) {\n for (let k = j + 1; k < n; k++) {\n const tri = polygon([h[i]!, h[j]!, h[k]!])\n if (!coversPoints(tri, points)) continue\n const a = area(tri)\n if (a < bestA) {\n bestA = a\n best = tri\n }\n }\n }\n }\n return best ?? convexHull(points)\n}\n\n/** Closed polygon connecting points in order of minimum spanning path heuristic (NN tour). */\nexport function minPerimeter(points: Vec2[]): Path {\n if (points.length === 0) return polygon([])\n if (points.length < 3) return polygon(points)\n const remaining = points.map((p) => ({ ...p }))\n const ordered: Vec2[] = [remaining.shift()!]\n while (remaining.length) {\n const last = ordered[ordered.length - 1]!\n let bestI = 0\n let bestD = Infinity\n for (let i = 0; i < remaining.length; i++) {\n const d = Math.hypot(\n remaining[i]!.x - last.x,\n remaining[i]!.y - last.y,\n )\n if (d < bestD) {\n bestD = d\n bestI = i\n }\n }\n ordered.push(remaining.splice(bestI, 1)[0]!)\n }\n return polygon(ordered)\n}\n\n/** Connect points sorted by x then y. */\nexport function horizontal(points: Vec2[]): Path {\n const pts = [...points].sort((a, b) =>\n a.y === b.y ? a.x - b.x : a.y - b.y,\n )\n return polygon(pts)\n}\n\n/** Connect points sorted by y then x. */\nexport function vertical(points: Vec2[]): Path {\n const pts = [...points].sort((a, b) =>\n a.x === b.x ? a.y - b.y : a.x - b.x,\n )\n return polygon(pts)\n}\n\n/** Connect by polar angle around centroid. */\nexport function angular(points: Vec2[]): Path {\n if (points.length === 0) return polygon([])\n const c = centroidOf(points)\n const pts = [...points].sort(\n (a, b) =>\n Math.atan2(a.y - c.y, a.x - c.x) - Math.atan2(b.y - c.y, b.x - c.x),\n )\n return polygon(pts)\n}\n\n/** Alias of angular. */\nexport function circular(points: Vec2[]): Path {\n return angular(points)\n}\n\n/**\n * Onion layers: successive convex hulls. Returns the outermost hull polygon;\n * use `onionLayers` for all peels.\n */\nexport function onion(points: Vec2[]): Path {\n const layers = onionLayers(points)\n return layers[0] ?? polygon([])\n}\n\n/** Convex onion peels from outside in. */\nexport function onionLayers(points: Vec2[]): Path[] {\n let remaining = points.map((p) => ({ ...p }))\n const layers: Path[] = []\n while (remaining.length >= 3) {\n const hull = convexHull(remaining)\n const ring = hull.rings[0] ?? []\n if (ring.length < 3) break\n layers.push(hull)\n const onHull = new Set(ring.map((v) => `${v.x},${v.y}`))\n remaining = remaining.filter((p) => !onHull.has(`${p.x},${p.y}`))\n }\n return layers\n}\n\n/** Connect points ordered by 2D Hilbert curve index. */\nexport function hilbert(points: Vec2[]): Path {\n return polygon(hilbertSort(points))\n}\n\nfunction coversPoints(poly: Path, points: Vec2[]): boolean {\n // triangle cover check via barycentric\n const r = poly.rings[0]\n if (!r || r.length < 3) return false\n const a = r[0]!\n const b = r[1]!\n const c = r[2]!\n for (const p of points) {\n if (!pointInTriangle(p, a, b, c)) return false\n }\n return true\n}\n\nfunction pointInTriangle(p: Vec2, a: Vec2, b: Vec2, c: Vec2): boolean {\n const v0x = c.x - a.x\n const v0y = c.y - a.y\n const v1x = b.x - a.x\n const v1y = b.y - a.y\n const v2x = p.x - a.x\n const v2y = p.y - a.y\n const dot00 = v0x * v0x + v0y * v0y\n const dot01 = v0x * v1x + v0y * v1y\n const dot02 = v0x * v2x + v0y * v2y\n const dot11 = v1x * v1x + v1y * v1y\n const dot12 = v1x * v2x + v1y * v2y\n const inv = 1 / (dot00 * dot11 - dot01 * dot01 || 1e-18)\n const u = (dot11 * dot02 - dot01 * dot12) * inv\n const v = (dot00 * dot12 - dot01 * dot02) * inv\n return u >= -1e-9 && v >= -1e-9 && u + v <= 1 + 1e-9\n}\n\nfunction centroidOf(points: Vec2[]): Vec2 {\n let sx = 0\n let sy = 0\n for (const p of points) {\n sx += p.x\n sy += p.y\n }\n return { x: sx / points.length, y: sy / points.length }\n}\n\nexport const polygonisation = {\n maxArea,\n minArea,\n minPerimeter,\n horizontal,\n vertical,\n angular,\n circular,\n onion,\n onionLayers,\n hilbert,\n}\n"]}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { V as Vec2 } from '../
|
|
2
|
-
import {
|
|
3
|
-
import { G as Group } from '../group-BNjVVYSQ.js';
|
|
1
|
+
import { P as Path, V as Vec2 } from '../path-Boeshk9L.js';
|
|
2
|
+
import { G as Group } from '../group-YVmSJuGo.js';
|
|
4
3
|
|
|
5
4
|
/** Signed area of a closed ring (shoelace). */
|
|
6
5
|
declare function ringArea(ring: Vec2[]): number;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { V as Vec2 } from '../
|
|
2
|
-
import { S as Segment } from '../segment-
|
|
3
|
-
import {
|
|
4
|
-
import { G as Group } from '../group-BNjVVYSQ.js';
|
|
1
|
+
import { P as Path, V as Vec2 } from '../path-Boeshk9L.js';
|
|
2
|
+
import { S as Segment } from '../segment-CpLILSd7.js';
|
|
3
|
+
import { G as Group } from '../group-YVmSJuGo.js';
|
|
5
4
|
|
|
6
5
|
/** Exterior ring as its own closed path (holes dropped). */
|
|
7
6
|
declare function extractPerimeter(p: Path): Path;
|