@adatechnology/game-shapes 0.0.0-stage → 0.1.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.
@@ -0,0 +1,2972 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+
5
+ // src/color.ts
6
+ function colorHex(color2, fallback = 16777215) {
7
+ if (color2 === void 0) return fallback;
8
+ if (typeof color2 === "number") return Math.floor(color2);
9
+ const match = /^#([0-9a-fA-F]{6})$/.exec(color2);
10
+ if (!match) throw new Error(`game-shapes: colour "${color2}" is not 0xRRGGBB or #rrggbb`);
11
+ return parseInt(match[1], 16);
12
+ }
13
+ var srgbToLinear = (c) => c < 0.04045 ? c * 0.0773993808 : Math.pow(c * 0.9478672986 + 0.0521327014, 2.4);
14
+ function colorChannels(hex, space) {
15
+ const r = (hex >> 16 & 255) / 255;
16
+ const g = (hex >> 8 & 255) / 255;
17
+ const b = (hex & 255) / 255;
18
+ return space === "linear" ? [srgbToLinear(r), srgbToLinear(g), srgbToLinear(b)] : [r, g, b];
19
+ }
20
+
21
+ // src/hash.ts
22
+ function canonicalJson(value) {
23
+ if (value === null) return "null";
24
+ if (typeof value === "number") return Object.is(value, -0) ? "-0" : Number.isFinite(value) ? String(value) : JSON.stringify(String(value));
25
+ if (typeof value === "string") return JSON.stringify(value);
26
+ if (typeof value === "boolean") return value ? "true" : "false";
27
+ if (typeof value === "function") throw new Error("game-shapes: a function cannot be part of a shape (register it as a generator)");
28
+ if (ArrayBuffer.isView(value)) return canonicalJson(Array.from(value));
29
+ if (Array.isArray(value)) return `[${value.map((item) => item === void 0 ? "null" : canonicalJson(item)).join(",")}]`;
30
+ if (typeof value === "object") {
31
+ const record = value;
32
+ const keys = Object.keys(record).filter((key) => record[key] !== void 0).sort();
33
+ return `{${keys.map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(",")}}`;
34
+ }
35
+ return "null";
36
+ }
37
+ var lane = (text, offset, prime) => {
38
+ let hash = offset;
39
+ for (let index = 0; index < text.length; index++) {
40
+ hash ^= text.charCodeAt(index);
41
+ hash = Math.imul(hash, prime);
42
+ }
43
+ return (hash >>> 0).toString(16).padStart(8, "0");
44
+ };
45
+ var hash64 = (text) => lane(text, 2166136261, 16777619) + lane(text, 84696351, 1540483477);
46
+ var hashValue = (value) => hash64(canonicalJson(value));
47
+
48
+ // src/math.ts
49
+ var identity4 = () => [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
50
+ function quaternionFromEuler(x, y, z, order = "XYZ") {
51
+ const cos = Math.cos;
52
+ const sin = Math.sin;
53
+ const c1 = cos(x / 2);
54
+ const c2 = cos(y / 2);
55
+ const c3 = cos(z / 2);
56
+ const s1 = sin(x / 2);
57
+ const s2 = sin(y / 2);
58
+ const s3 = sin(z / 2);
59
+ switch (order) {
60
+ case "XYZ":
61
+ return [s1 * c2 * c3 + c1 * s2 * s3, c1 * s2 * c3 - s1 * c2 * s3, c1 * c2 * s3 + s1 * s2 * c3, c1 * c2 * c3 - s1 * s2 * s3];
62
+ case "YXZ":
63
+ return [s1 * c2 * c3 + c1 * s2 * s3, c1 * s2 * c3 - s1 * c2 * s3, c1 * c2 * s3 - s1 * s2 * c3, c1 * c2 * c3 + s1 * s2 * s3];
64
+ case "ZXY":
65
+ return [s1 * c2 * c3 - c1 * s2 * s3, c1 * s2 * c3 + s1 * c2 * s3, c1 * c2 * s3 + s1 * s2 * c3, c1 * c2 * c3 - s1 * s2 * s3];
66
+ case "ZYX":
67
+ return [s1 * c2 * c3 - c1 * s2 * s3, c1 * s2 * c3 + s1 * c2 * s3, c1 * c2 * s3 - s1 * s2 * c3, c1 * c2 * c3 + s1 * s2 * s3];
68
+ case "YZX":
69
+ return [s1 * c2 * c3 + c1 * s2 * s3, c1 * s2 * c3 + s1 * c2 * s3, c1 * c2 * s3 - s1 * s2 * c3, c1 * c2 * c3 - s1 * s2 * s3];
70
+ case "XZY":
71
+ return [s1 * c2 * c3 - c1 * s2 * s3, c1 * s2 * c3 - s1 * c2 * s3, c1 * c2 * s3 + s1 * s2 * c3, c1 * c2 * c3 + s1 * s2 * s3];
72
+ default:
73
+ throw new Error(`game-shapes: unknown rotation order "${String(order)}"`);
74
+ }
75
+ }
76
+ function compose(px, py, pz, q, sx, sy, sz) {
77
+ const [x, y, z, w] = q;
78
+ const x2 = x + x;
79
+ const y2 = y + y;
80
+ const z2 = z + z;
81
+ const xx = x * x2;
82
+ const xy = x * y2;
83
+ const xz = x * z2;
84
+ const yy = y * y2;
85
+ const yz = y * z2;
86
+ const zz = z * z2;
87
+ const wx = w * x2;
88
+ const wy = w * y2;
89
+ const wz = w * z2;
90
+ return [
91
+ (1 - (yy + zz)) * sx,
92
+ (xy + wz) * sx,
93
+ (xz - wy) * sx,
94
+ 0,
95
+ (xy - wz) * sy,
96
+ (1 - (xx + zz)) * sy,
97
+ (yz + wx) * sy,
98
+ 0,
99
+ (xz + wy) * sz,
100
+ (yz - wx) * sz,
101
+ (1 - (xx + yy)) * sz,
102
+ 0,
103
+ px,
104
+ py,
105
+ pz,
106
+ 1
107
+ ];
108
+ }
109
+ function headingMatrix(theta, x = 0, y = 0, z = 0) {
110
+ const c = Math.cos(theta);
111
+ const s = Math.sin(theta);
112
+ return [c, 0, -s, 0, 0, 1, 0, 0, s, 0, c, 0, x, y, z, 1];
113
+ }
114
+ function multiply(ae, be) {
115
+ const a11 = ae[0], a12 = ae[4], a13 = ae[8], a14 = ae[12];
116
+ const a21 = ae[1], a22 = ae[5], a23 = ae[9], a24 = ae[13];
117
+ const a31 = ae[2], a32 = ae[6], a33 = ae[10], a34 = ae[14];
118
+ const a41 = ae[3], a42 = ae[7], a43 = ae[11], a44 = ae[15];
119
+ const b11 = be[0], b12 = be[4], b13 = be[8], b14 = be[12];
120
+ const b21 = be[1], b22 = be[5], b23 = be[9], b24 = be[13];
121
+ const b31 = be[2], b32 = be[6], b33 = be[10], b34 = be[14];
122
+ const b41 = be[3], b42 = be[7], b43 = be[11], b44 = be[15];
123
+ return [
124
+ a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41,
125
+ a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41,
126
+ a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41,
127
+ a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41,
128
+ a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42,
129
+ a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42,
130
+ a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42,
131
+ a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42,
132
+ a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43,
133
+ a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43,
134
+ a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43,
135
+ a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43,
136
+ a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44,
137
+ a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44,
138
+ a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44,
139
+ a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44
140
+ ];
141
+ }
142
+ function normalMatrix(me) {
143
+ const n11 = me[0], n21 = me[1], n31 = me[2];
144
+ const n12 = me[4], n22 = me[5], n32 = me[6];
145
+ const n13 = me[8], n23 = me[9], n33 = me[10];
146
+ const t11 = n33 * n22 - n32 * n23;
147
+ const t12 = n32 * n13 - n33 * n12;
148
+ const t13 = n23 * n12 - n22 * n13;
149
+ const det = n11 * t11 + n21 * t12 + n31 * t13;
150
+ if (det === 0) return [0, 0, 0, 0, 0, 0, 0, 0, 0];
151
+ const detInv = 1 / det;
152
+ const te = [
153
+ t11 * detInv,
154
+ (n31 * n23 - n33 * n21) * detInv,
155
+ (n32 * n21 - n31 * n22) * detInv,
156
+ t12 * detInv,
157
+ (n33 * n11 - n31 * n13) * detInv,
158
+ (n31 * n12 - n32 * n11) * detInv,
159
+ t13 * detInv,
160
+ (n21 * n13 - n23 * n11) * detInv,
161
+ (n22 * n11 - n21 * n12) * detInv
162
+ ];
163
+ let tmp = te[1];
164
+ te[1] = te[3];
165
+ te[3] = tmp;
166
+ tmp = te[2];
167
+ te[2] = te[6];
168
+ te[6] = tmp;
169
+ tmp = te[5];
170
+ te[5] = te[7];
171
+ te[7] = tmp;
172
+ return te;
173
+ }
174
+ var isAffine = (e) => e[3] === 0 && e[7] === 0 && e[11] === 0 && e[15] === 1;
175
+ function transformPoint(e, x, y, z) {
176
+ const w = 1 / (e[3] * x + e[7] * y + e[11] * z + e[15]);
177
+ return [(e[0] * x + e[4] * y + e[8] * z + e[12]) * w, (e[1] * x + e[5] * y + e[9] * z + e[13]) * w, (e[2] * x + e[6] * y + e[10] * z + e[14]) * w];
178
+ }
179
+ var toVec3 = (scale) => scale === void 0 ? [1, 1, 1] : typeof scale === "number" ? [scale, scale, scale] : scale;
180
+ function matrixOf(t) {
181
+ if (t.matrix !== void 0) return [...t.matrix];
182
+ if (t.heading !== void 0) {
183
+ const p2 = t.position ?? [0, 0, 0];
184
+ return headingMatrix(t.heading, p2[0], p2[1], p2[2]);
185
+ }
186
+ if (t.position === void 0 && t.rotation === void 0 && t.scale === void 0) return null;
187
+ const p = t.position ?? [0, 0, 0];
188
+ const r = t.rotation ?? [0, 0, 0];
189
+ const s = toVec3(t.scale);
190
+ return compose(p[0], p[1], p[2], quaternionFromEuler(r[0], r[1], r[2], t.rotationOrder ?? "XYZ"), s[0], s[1], s[2]);
191
+ }
192
+ var asMat4 = (m) => m ? [...m] : identity4();
193
+
194
+ // src/primitives/three-generators.ts
195
+ function needsUint32(array) {
196
+ for (let i = array.length - 1; i >= 0; --i) if (array[i] >= 65535) return true;
197
+ return false;
198
+ }
199
+ var indexArray = (indices) => needsUint32(indices) ? new Uint32Array(indices) : new Uint16Array(indices);
200
+ function pack(type, parameters, a) {
201
+ return {
202
+ type,
203
+ parameters,
204
+ positions: new Float32Array(a.vertices),
205
+ normals: new Float32Array(a.normals),
206
+ uvs: new Float32Array(a.uvs),
207
+ ...a.indices ? { indices: indexArray(a.indices) } : {},
208
+ groups: a.groups
209
+ };
210
+ }
211
+ var normalize3 = (x, y, z) => {
212
+ const length = Math.sqrt(x * x + y * y + z * z);
213
+ const s = 1 / (length || 1);
214
+ return [x * s, y * s, z * s];
215
+ };
216
+ function box(width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1) {
217
+ const parameters = { width, height, depth, widthSegments, heightSegments, depthSegments };
218
+ widthSegments = Math.floor(widthSegments);
219
+ heightSegments = Math.floor(heightSegments);
220
+ depthSegments = Math.floor(depthSegments);
221
+ const indices = [];
222
+ const vertices = [];
223
+ const normals = [];
224
+ const uvs = [];
225
+ const groups = [];
226
+ let numberOfVertices = 0;
227
+ let groupStart = 0;
228
+ const buildPlane = (u, v, w, udir, vdir, width2, height2, depth2, gridX, gridY, materialIndex) => {
229
+ const segmentWidth = width2 / gridX;
230
+ const segmentHeight = height2 / gridY;
231
+ const widthHalf = width2 / 2;
232
+ const heightHalf = height2 / 2;
233
+ const depthHalf = depth2 / 2;
234
+ const gridX1 = gridX + 1;
235
+ const gridY1 = gridY + 1;
236
+ let vertexCounter = 0;
237
+ let groupCount = 0;
238
+ const vector = { x: 0, y: 0, z: 0 };
239
+ for (let iy = 0; iy < gridY1; iy++) {
240
+ const y = iy * segmentHeight - heightHalf;
241
+ for (let ix = 0; ix < gridX1; ix++) {
242
+ const x = ix * segmentWidth - widthHalf;
243
+ vector[u] = x * udir;
244
+ vector[v] = y * vdir;
245
+ vector[w] = depthHalf;
246
+ vertices.push(vector.x, vector.y, vector.z);
247
+ vector[u] = 0;
248
+ vector[v] = 0;
249
+ vector[w] = depth2 > 0 ? 1 : -1;
250
+ normals.push(vector.x, vector.y, vector.z);
251
+ uvs.push(ix / gridX);
252
+ uvs.push(1 - iy / gridY);
253
+ vertexCounter += 1;
254
+ }
255
+ }
256
+ for (let iy = 0; iy < gridY; iy++) {
257
+ for (let ix = 0; ix < gridX; ix++) {
258
+ const a = numberOfVertices + ix + gridX1 * iy;
259
+ const b = numberOfVertices + ix + gridX1 * (iy + 1);
260
+ const c = numberOfVertices + (ix + 1) + gridX1 * (iy + 1);
261
+ const d = numberOfVertices + (ix + 1) + gridX1 * iy;
262
+ indices.push(a, b, d);
263
+ indices.push(b, c, d);
264
+ groupCount += 6;
265
+ }
266
+ }
267
+ groups.push({ start: groupStart, count: groupCount, materialIndex });
268
+ groupStart += groupCount;
269
+ numberOfVertices += vertexCounter;
270
+ };
271
+ buildPlane("z", "y", "x", -1, -1, depth, height, width, depthSegments, heightSegments, 0);
272
+ buildPlane("z", "y", "x", 1, -1, depth, height, -width, depthSegments, heightSegments, 1);
273
+ buildPlane("x", "z", "y", 1, 1, width, depth, height, widthSegments, depthSegments, 2);
274
+ buildPlane("x", "z", "y", 1, -1, width, depth, -height, widthSegments, depthSegments, 3);
275
+ buildPlane("x", "y", "z", 1, -1, width, height, depth, widthSegments, heightSegments, 4);
276
+ buildPlane("x", "y", "z", -1, -1, width, height, -depth, widthSegments, heightSegments, 5);
277
+ return pack("BoxGeometry", parameters, { vertices, normals, uvs, indices, groups });
278
+ }
279
+ function cylinderArrays(radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength) {
280
+ radialSegments = Math.floor(radialSegments);
281
+ heightSegments = Math.floor(heightSegments);
282
+ const indices = [];
283
+ const vertices = [];
284
+ const normals = [];
285
+ const uvs = [];
286
+ const groups = [];
287
+ let index = 0;
288
+ const indexArray2 = [];
289
+ const halfHeight = height / 2;
290
+ let groupStart = 0;
291
+ const generateTorso = () => {
292
+ let groupCount = 0;
293
+ const slope = (radiusBottom - radiusTop) / height;
294
+ for (let y = 0; y <= heightSegments; y++) {
295
+ const indexRow = [];
296
+ const v = y / heightSegments;
297
+ const radius = v * (radiusBottom - radiusTop) + radiusTop;
298
+ for (let x = 0; x <= radialSegments; x++) {
299
+ const u = x / radialSegments;
300
+ const theta = u * thetaLength + thetaStart;
301
+ const sinTheta = Math.sin(theta);
302
+ const cosTheta = Math.cos(theta);
303
+ vertices.push(radius * sinTheta, -v * height + halfHeight, radius * cosTheta);
304
+ const n = normalize3(sinTheta, slope, cosTheta);
305
+ normals.push(n[0], n[1], n[2]);
306
+ uvs.push(u, 1 - v);
307
+ indexRow.push(index++);
308
+ }
309
+ indexArray2.push(indexRow);
310
+ }
311
+ for (let x = 0; x < radialSegments; x++) {
312
+ for (let y = 0; y < heightSegments; y++) {
313
+ const a = indexArray2[y][x];
314
+ const b = indexArray2[y + 1][x];
315
+ const c = indexArray2[y + 1][x + 1];
316
+ const d = indexArray2[y][x + 1];
317
+ indices.push(a, b, d);
318
+ indices.push(b, c, d);
319
+ groupCount += 6;
320
+ }
321
+ }
322
+ groups.push({ start: groupStart, count: groupCount, materialIndex: 0 });
323
+ groupStart += groupCount;
324
+ };
325
+ const generateCap = (top) => {
326
+ const centerIndexStart = index;
327
+ let groupCount = 0;
328
+ const radius = top === true ? radiusTop : radiusBottom;
329
+ const sign2 = top === true ? 1 : -1;
330
+ for (let x = 1; x <= radialSegments; x++) {
331
+ vertices.push(0, halfHeight * sign2, 0);
332
+ normals.push(0, sign2, 0);
333
+ uvs.push(0.5, 0.5);
334
+ index++;
335
+ }
336
+ const centerIndexEnd = index;
337
+ for (let x = 0; x <= radialSegments; x++) {
338
+ const u = x / radialSegments;
339
+ const theta = u * thetaLength + thetaStart;
340
+ const cosTheta = Math.cos(theta);
341
+ const sinTheta = Math.sin(theta);
342
+ vertices.push(radius * sinTheta, halfHeight * sign2, radius * cosTheta);
343
+ normals.push(0, sign2, 0);
344
+ uvs.push(cosTheta * 0.5 + 0.5, sinTheta * 0.5 * sign2 + 0.5);
345
+ index++;
346
+ }
347
+ for (let x = 0; x < radialSegments; x++) {
348
+ const c = centerIndexStart + x;
349
+ const i = centerIndexEnd + x;
350
+ if (top === true) indices.push(i, i + 1, c);
351
+ else indices.push(i + 1, i, c);
352
+ groupCount += 3;
353
+ }
354
+ groups.push({ start: groupStart, count: groupCount, materialIndex: top === true ? 1 : 2 });
355
+ groupStart += groupCount;
356
+ };
357
+ generateTorso();
358
+ if (openEnded === false) {
359
+ if (radiusTop > 0) generateCap(true);
360
+ if (radiusBottom > 0) generateCap(false);
361
+ }
362
+ return { vertices, normals, uvs, indices, groups };
363
+ }
364
+ function cylinder(radiusTop = 1, radiusBottom = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2) {
365
+ const parameters = { radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength };
366
+ return pack("CylinderGeometry", parameters, cylinderArrays(radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength));
367
+ }
368
+ function cone(radius = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2) {
369
+ const parameters = { radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength };
370
+ return pack("ConeGeometry", parameters, cylinderArrays(0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength));
371
+ }
372
+ function sphere(radius = 1, widthSegments = 32, heightSegments = 16, phiStart = 0, phiLength = Math.PI * 2, thetaStart = 0, thetaLength = Math.PI) {
373
+ const parameters = { radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength };
374
+ widthSegments = Math.max(3, Math.floor(widthSegments));
375
+ heightSegments = Math.max(2, Math.floor(heightSegments));
376
+ const thetaEnd = Math.min(thetaStart + thetaLength, Math.PI);
377
+ let index = 0;
378
+ const grid = [];
379
+ const indices = [];
380
+ const vertices = [];
381
+ const normals = [];
382
+ const uvs = [];
383
+ for (let iy = 0; iy <= heightSegments; iy++) {
384
+ const verticesRow = [];
385
+ const v = iy / heightSegments;
386
+ let uOffset = 0;
387
+ if (iy === 0 && thetaStart === 0) uOffset = 0.5 / widthSegments;
388
+ else if (iy === heightSegments && thetaEnd === Math.PI) uOffset = -0.5 / widthSegments;
389
+ for (let ix = 0; ix <= widthSegments; ix++) {
390
+ const u = ix / widthSegments;
391
+ const x = -radius * Math.cos(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength);
392
+ const y = radius * Math.cos(thetaStart + v * thetaLength);
393
+ const z = radius * Math.sin(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength);
394
+ vertices.push(x, y, z);
395
+ const n = normalize3(x, y, z);
396
+ normals.push(n[0], n[1], n[2]);
397
+ uvs.push(u + uOffset, 1 - v);
398
+ verticesRow.push(index++);
399
+ }
400
+ grid.push(verticesRow);
401
+ }
402
+ for (let iy = 0; iy < heightSegments; iy++) {
403
+ for (let ix = 0; ix < widthSegments; ix++) {
404
+ const a = grid[iy][ix + 1];
405
+ const b = grid[iy][ix];
406
+ const c = grid[iy + 1][ix];
407
+ const d = grid[iy + 1][ix + 1];
408
+ if (iy !== 0 || thetaStart > 0) indices.push(a, b, d);
409
+ if (iy !== heightSegments - 1 || thetaEnd < Math.PI) indices.push(b, c, d);
410
+ }
411
+ }
412
+ return pack("SphereGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
413
+ }
414
+ function plane(width = 1, height = 1, widthSegments = 1, heightSegments = 1) {
415
+ const parameters = { width, height, widthSegments, heightSegments };
416
+ const widthHalf = width / 2;
417
+ const heightHalf = height / 2;
418
+ const gridX = Math.floor(widthSegments);
419
+ const gridY = Math.floor(heightSegments);
420
+ const gridX1 = gridX + 1;
421
+ const gridY1 = gridY + 1;
422
+ const segmentWidth = width / gridX;
423
+ const segmentHeight = height / gridY;
424
+ const indices = [];
425
+ const vertices = [];
426
+ const normals = [];
427
+ const uvs = [];
428
+ for (let iy = 0; iy < gridY1; iy++) {
429
+ const y = iy * segmentHeight - heightHalf;
430
+ for (let ix = 0; ix < gridX1; ix++) {
431
+ const x = ix * segmentWidth - widthHalf;
432
+ vertices.push(x, -y, 0);
433
+ normals.push(0, 0, 1);
434
+ uvs.push(ix / gridX);
435
+ uvs.push(1 - iy / gridY);
436
+ }
437
+ }
438
+ for (let iy = 0; iy < gridY; iy++) {
439
+ for (let ix = 0; ix < gridX; ix++) {
440
+ const a = ix + gridX1 * iy;
441
+ const b = ix + gridX1 * (iy + 1);
442
+ const c = ix + 1 + gridX1 * (iy + 1);
443
+ const d = ix + 1 + gridX1 * iy;
444
+ indices.push(a, b, d);
445
+ indices.push(b, c, d);
446
+ }
447
+ }
448
+ return pack("PlaneGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
449
+ }
450
+ function circle(radius = 1, segments = 32, thetaStart = 0, thetaLength = Math.PI * 2) {
451
+ const parameters = { radius, segments, thetaStart, thetaLength };
452
+ segments = Math.max(3, segments);
453
+ const indices = [];
454
+ const vertices = [];
455
+ const normals = [];
456
+ const uvs = [];
457
+ vertices.push(0, 0, 0);
458
+ normals.push(0, 0, 1);
459
+ uvs.push(0.5, 0.5);
460
+ for (let s = 0, i = 3; s <= segments; s++, i += 3) {
461
+ const segment = thetaStart + s / segments * thetaLength;
462
+ vertices.push(radius * Math.cos(segment), radius * Math.sin(segment), 0);
463
+ normals.push(0, 0, 1);
464
+ uvs.push((vertices[i] / radius + 1) / 2, (vertices[i + 1] / radius + 1) / 2);
465
+ }
466
+ for (let i = 1; i <= segments; i++) indices.push(i, i + 1, 0);
467
+ return pack("CircleGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
468
+ }
469
+ function ring(innerRadius = 0.5, outerRadius = 1, thetaSegments = 32, phiSegments = 1, thetaStart = 0, thetaLength = Math.PI * 2) {
470
+ const parameters = { innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength };
471
+ thetaSegments = Math.max(3, thetaSegments);
472
+ phiSegments = Math.max(1, phiSegments);
473
+ const indices = [];
474
+ const vertices = [];
475
+ const normals = [];
476
+ const uvs = [];
477
+ let radius = innerRadius;
478
+ const radiusStep = (outerRadius - innerRadius) / phiSegments;
479
+ for (let j = 0; j <= phiSegments; j++) {
480
+ for (let i = 0; i <= thetaSegments; i++) {
481
+ const segment = thetaStart + i / thetaSegments * thetaLength;
482
+ const x = radius * Math.cos(segment);
483
+ const y = radius * Math.sin(segment);
484
+ vertices.push(x, y, 0);
485
+ normals.push(0, 0, 1);
486
+ uvs.push((x / outerRadius + 1) / 2, (y / outerRadius + 1) / 2);
487
+ }
488
+ radius += radiusStep;
489
+ }
490
+ for (let j = 0; j < phiSegments; j++) {
491
+ const thetaSegmentLevel = j * (thetaSegments + 1);
492
+ for (let i = 0; i < thetaSegments; i++) {
493
+ const segment = i + thetaSegmentLevel;
494
+ const a = segment;
495
+ const b = segment + thetaSegments + 1;
496
+ const c = segment + thetaSegments + 2;
497
+ const d = segment + 1;
498
+ indices.push(a, b, d);
499
+ indices.push(b, c, d);
500
+ }
501
+ }
502
+ return pack("RingGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
503
+ }
504
+ function lathe(points, segments = 12, phiStart = 0, phiLength = Math.PI * 2) {
505
+ const parameters = { points: points.map(([x, y]) => ({ x, y })), segments, phiStart, phiLength };
506
+ segments = Math.floor(segments);
507
+ phiLength = Math.max(0, Math.min(Math.PI * 2, phiLength));
508
+ const indices = [];
509
+ const vertices = [];
510
+ const uvs = [];
511
+ const initNormals = [];
512
+ const normals = [];
513
+ const inverseSegments = 1 / segments;
514
+ const normal = { x: 0, y: 0, z: 0 };
515
+ const curNormal = { x: 0, y: 0, z: 0 };
516
+ const prevNormal = { x: 0, y: 0, z: 0 };
517
+ let dx = 0;
518
+ let dy = 0;
519
+ const last = points.length - 1;
520
+ const px = (j) => points[j][0];
521
+ const py = (j) => points[j][1];
522
+ const setNormalized = () => {
523
+ const n = normalize3(normal.x, normal.y, normal.z);
524
+ normal.x = n[0];
525
+ normal.y = n[1];
526
+ normal.z = n[2];
527
+ };
528
+ for (let j = 0; j <= last; j++) {
529
+ if (j === 0) {
530
+ dx = px(j + 1) - px(j);
531
+ dy = py(j + 1) - py(j);
532
+ normal.x = dy * 1;
533
+ normal.y = -dx;
534
+ normal.z = dy * 0;
535
+ prevNormal.x = normal.x;
536
+ prevNormal.y = normal.y;
537
+ prevNormal.z = normal.z;
538
+ setNormalized();
539
+ initNormals.push(normal.x, normal.y, normal.z);
540
+ } else if (j === last) {
541
+ initNormals.push(prevNormal.x, prevNormal.y, prevNormal.z);
542
+ } else {
543
+ dx = px(j + 1) - px(j);
544
+ dy = py(j + 1) - py(j);
545
+ normal.x = dy * 1;
546
+ normal.y = -dx;
547
+ normal.z = dy * 0;
548
+ curNormal.x = normal.x;
549
+ curNormal.y = normal.y;
550
+ curNormal.z = normal.z;
551
+ normal.x += prevNormal.x;
552
+ normal.y += prevNormal.y;
553
+ normal.z += prevNormal.z;
554
+ setNormalized();
555
+ initNormals.push(normal.x, normal.y, normal.z);
556
+ prevNormal.x = curNormal.x;
557
+ prevNormal.y = curNormal.y;
558
+ prevNormal.z = curNormal.z;
559
+ }
560
+ }
561
+ for (let i = 0; i <= segments; i++) {
562
+ const phi = phiStart + i * inverseSegments * phiLength;
563
+ const sin = Math.sin(phi);
564
+ const cos = Math.cos(phi);
565
+ for (let j = 0; j <= last; j++) {
566
+ vertices.push(px(j) * sin, py(j), px(j) * cos);
567
+ uvs.push(i / segments, j / (points.length - 1));
568
+ const x = initNormals[3 * j + 0] * sin;
569
+ const y = initNormals[3 * j + 1];
570
+ const z = initNormals[3 * j + 0] * cos;
571
+ normals.push(x, y, z);
572
+ }
573
+ }
574
+ for (let i = 0; i < segments; i++) {
575
+ for (let j = 0; j < points.length - 1; j++) {
576
+ const base = j + i * points.length;
577
+ const a = base;
578
+ const b = base + points.length;
579
+ const c = base + points.length + 1;
580
+ const d = base + 1;
581
+ indices.push(a, b, d);
582
+ indices.push(c, d, b);
583
+ }
584
+ }
585
+ return pack("LatheGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
586
+ }
587
+ function torus(radius = 1, tube = 0.4, radialSegments = 12, tubularSegments = 48, arc = Math.PI * 2) {
588
+ const parameters = { radius, tube, radialSegments, tubularSegments, arc };
589
+ radialSegments = Math.floor(radialSegments);
590
+ tubularSegments = Math.floor(tubularSegments);
591
+ const indices = [];
592
+ const vertices = [];
593
+ const normals = [];
594
+ const uvs = [];
595
+ for (let j = 0; j <= radialSegments; j++) {
596
+ for (let i = 0; i <= tubularSegments; i++) {
597
+ const u = i / tubularSegments * arc;
598
+ const v = j / radialSegments * Math.PI * 2;
599
+ const x = (radius + tube * Math.cos(v)) * Math.cos(u);
600
+ const y = (radius + tube * Math.cos(v)) * Math.sin(u);
601
+ const z = tube * Math.sin(v);
602
+ vertices.push(x, y, z);
603
+ const cx = radius * Math.cos(u);
604
+ const cy = radius * Math.sin(u);
605
+ const n = normalize3(x - cx, y - cy, z - 0);
606
+ normals.push(n[0], n[1], n[2]);
607
+ uvs.push(i / tubularSegments);
608
+ uvs.push(j / radialSegments);
609
+ }
610
+ }
611
+ for (let j = 1; j <= radialSegments; j++) {
612
+ for (let i = 1; i <= tubularSegments; i++) {
613
+ const a = (tubularSegments + 1) * j + i - 1;
614
+ const b = (tubularSegments + 1) * (j - 1) + i - 1;
615
+ const c = (tubularSegments + 1) * (j - 1) + i;
616
+ const d = (tubularSegments + 1) * j + i;
617
+ indices.push(a, b, d);
618
+ indices.push(b, c, d);
619
+ }
620
+ }
621
+ return pack("TorusGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
622
+ }
623
+
624
+ // src/primitives/difference.ts
625
+ function difference(size, subtract) {
626
+ const [w, h, d] = size;
627
+ const hw = w / 2;
628
+ const hh = h / 2;
629
+ const hd = d / 2;
630
+ const holes = subtract.map((hole) => ({ x0: Math.max(-hw, Math.min(hole.min[0], hole.max[0])), x1: Math.min(hw, Math.max(hole.min[0], hole.max[0])), y0: Math.max(-hh, Math.min(hole.min[1], hole.max[1])), y1: Math.min(hh, Math.max(hole.min[1], hole.max[1])) })).filter((hole) => hole.x1 > hole.x0 && hole.y1 > hole.y0);
631
+ const cuts = (lo, hi, values) => [.../* @__PURE__ */ new Set([lo, hi, ...values.filter((v) => v > lo && v < hi)])].sort((a, b) => a - b);
632
+ const xs = cuts(-hw, hw, holes.flatMap((hole) => [hole.x0, hole.x1]));
633
+ const ys = cuts(-hh, hh, holes.flatMap((hole) => [hole.y0, hole.y1]));
634
+ const nx = xs.length - 1;
635
+ const ny = ys.length - 1;
636
+ const solid = (i, j) => {
637
+ if (i < 0 || j < 0 || i >= nx || j >= ny) return false;
638
+ const cx = (xs[i] + xs[i + 1]) / 2;
639
+ const cy = (ys[j] + ys[j + 1]) / 2;
640
+ return !holes.some((hole) => cx > hole.x0 && cx < hole.x1 && cy > hole.y0 && cy < hole.y1);
641
+ };
642
+ const positions = [];
643
+ const normals = [];
644
+ const uvs = [];
645
+ const indices = [];
646
+ const quad = (corners, normal, uv) => {
647
+ const base = positions.length / 3;
648
+ for (let k = 0; k < 4; k++) {
649
+ positions.push(...corners[k]);
650
+ normals.push(...normal);
651
+ uvs.push(...uv[k]);
652
+ }
653
+ indices.push(base, base + 1, base + 2, base, base + 2, base + 3);
654
+ };
655
+ const fu = (x) => (x + hw) / w;
656
+ const fv = (y) => (y + hh) / h;
657
+ for (let j = 0; j < ny; j++) {
658
+ for (let i = 0; i < nx; i++) {
659
+ if (!solid(i, j)) continue;
660
+ const x0 = xs[i];
661
+ const x1 = xs[i + 1];
662
+ const y0 = ys[j];
663
+ const y1 = ys[j + 1];
664
+ quad([[x0, y0, hd], [x1, y0, hd], [x1, y1, hd], [x0, y1, hd]], [0, 0, 1], [[fu(x0), fv(y0)], [fu(x1), fv(y0)], [fu(x1), fv(y1)], [fu(x0), fv(y1)]]);
665
+ quad([[x1, y0, -hd], [x0, y0, -hd], [x0, y1, -hd], [x1, y1, -hd]], [0, 0, -1], [[1 - fu(x1), fv(y0)], [1 - fu(x0), fv(y0)], [1 - fu(x0), fv(y1)], [1 - fu(x1), fv(y1)]]);
666
+ const zu = (z) => (z + hd) / d;
667
+ if (!solid(i - 1, j)) quad([[x0, y0, -hd], [x0, y0, hd], [x0, y1, hd], [x0, y1, -hd]], [-1, 0, 0], [[zu(-hd), fv(y0)], [zu(hd), fv(y0)], [zu(hd), fv(y1)], [zu(-hd), fv(y1)]]);
668
+ if (!solid(i + 1, j)) quad([[x1, y0, hd], [x1, y0, -hd], [x1, y1, -hd], [x1, y1, hd]], [1, 0, 0], [[zu(hd), fv(y0)], [zu(-hd), fv(y0)], [zu(-hd), fv(y1)], [zu(hd), fv(y1)]]);
669
+ if (!solid(i, j - 1)) quad([[x0, y0, -hd], [x1, y0, -hd], [x1, y0, hd], [x0, y0, hd]], [0, -1, 0], [[fu(x0), zu(-hd)], [fu(x1), zu(-hd)], [fu(x1), zu(hd)], [fu(x0), zu(hd)]]);
670
+ if (!solid(i, j + 1)) quad([[x0, y1, hd], [x1, y1, hd], [x1, y1, -hd], [x0, y1, -hd]], [0, 1, 0], [[fu(x0), zu(hd)], [fu(x1), zu(hd)], [fu(x1), zu(-hd)], [fu(x0), zu(-hd)]]);
671
+ }
672
+ }
673
+ return {
674
+ type: "BufferGeometry",
675
+ parameters: { size: [w, h, d], subtract: holes.map((hole) => ({ min: [hole.x0, hole.y0], max: [hole.x1, hole.y1] })) },
676
+ positions: new Float32Array(positions),
677
+ normals: new Float32Array(normals),
678
+ uvs: new Float32Array(uvs),
679
+ indices: indexArray(indices),
680
+ groups: []
681
+ };
682
+ }
683
+
684
+ // src/primitives/earcut.ts
685
+ var Earcut = {
686
+ triangulate: function(data, holeIndices, dim = 2) {
687
+ const hasHoles = holeIndices && holeIndices.length;
688
+ const outerLen = hasHoles ? holeIndices[0] * dim : data.length;
689
+ let outerNode = linkedList(data, 0, outerLen, dim, true);
690
+ const triangles = [];
691
+ if (!outerNode || outerNode.next === outerNode.prev) return triangles;
692
+ let minX, minY, maxX, maxY, x, y, invSize;
693
+ if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim);
694
+ if (data.length > 80 * dim) {
695
+ minX = maxX = data[0];
696
+ minY = maxY = data[1];
697
+ for (let i = dim; i < outerLen; i += dim) {
698
+ x = data[i];
699
+ y = data[i + 1];
700
+ if (x < minX) minX = x;
701
+ if (y < minY) minY = y;
702
+ if (x > maxX) maxX = x;
703
+ if (y > maxY) maxY = y;
704
+ }
705
+ invSize = Math.max(maxX - minX, maxY - minY);
706
+ invSize = invSize !== 0 ? 32767 / invSize : 0;
707
+ }
708
+ earcutLinked(outerNode, triangles, dim, minX, minY, invSize, 0);
709
+ return triangles;
710
+ }
711
+ };
712
+ function linkedList(data, start, end, dim, clockwise) {
713
+ let i, last;
714
+ if (clockwise === signedArea(data, start, end, dim) > 0) {
715
+ for (i = start; i < end; i += dim) last = insertNode(i, data[i], data[i + 1], last);
716
+ } else {
717
+ for (i = end - dim; i >= start; i -= dim) last = insertNode(i, data[i], data[i + 1], last);
718
+ }
719
+ if (last && equals(last, last.next)) {
720
+ removeNode(last);
721
+ last = last.next;
722
+ }
723
+ return last;
724
+ }
725
+ function filterPoints(start, end) {
726
+ if (!start) return start;
727
+ if (!end) end = start;
728
+ let p = start, again;
729
+ do {
730
+ again = false;
731
+ if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) {
732
+ removeNode(p);
733
+ p = end = p.prev;
734
+ if (p === p.next) break;
735
+ again = true;
736
+ } else {
737
+ p = p.next;
738
+ }
739
+ } while (again || p !== end);
740
+ return end;
741
+ }
742
+ function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) {
743
+ if (!ear) return;
744
+ if (!pass && invSize) indexCurve(ear, minX, minY, invSize);
745
+ let stop = ear, prev, next;
746
+ while (ear.prev !== ear.next) {
747
+ prev = ear.prev;
748
+ next = ear.next;
749
+ if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) {
750
+ triangles.push(prev.i / dim | 0);
751
+ triangles.push(ear.i / dim | 0);
752
+ triangles.push(next.i / dim | 0);
753
+ removeNode(ear);
754
+ ear = next.next;
755
+ stop = next.next;
756
+ continue;
757
+ }
758
+ ear = next;
759
+ if (ear === stop) {
760
+ if (!pass) {
761
+ earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1);
762
+ } else if (pass === 1) {
763
+ ear = cureLocalIntersections(filterPoints(ear), triangles, dim);
764
+ earcutLinked(ear, triangles, dim, minX, minY, invSize, 2);
765
+ } else if (pass === 2) {
766
+ splitEarcut(ear, triangles, dim, minX, minY, invSize);
767
+ }
768
+ break;
769
+ }
770
+ }
771
+ }
772
+ function isEar(ear) {
773
+ const a = ear.prev, b = ear, c = ear.next;
774
+ if (area(a, b, c) >= 0) return false;
775
+ const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
776
+ const x0 = ax < bx ? ax < cx ? ax : cx : bx < cx ? bx : cx, y0 = ay < by ? ay < cy ? ay : cy : by < cy ? by : cy, x1 = ax > bx ? ax > cx ? ax : cx : bx > cx ? bx : cx, y1 = ay > by ? ay > cy ? ay : cy : by > cy ? by : cy;
777
+ let p = c.next;
778
+ while (p !== a) {
779
+ if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
780
+ p = p.next;
781
+ }
782
+ return true;
783
+ }
784
+ function isEarHashed(ear, minX, minY, invSize) {
785
+ const a = ear.prev, b = ear, c = ear.next;
786
+ if (area(a, b, c) >= 0) return false;
787
+ const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
788
+ const x0 = ax < bx ? ax < cx ? ax : cx : bx < cx ? bx : cx, y0 = ay < by ? ay < cy ? ay : cy : by < cy ? by : cy, x1 = ax > bx ? ax > cx ? ax : cx : bx > cx ? bx : cx, y1 = ay > by ? ay > cy ? ay : cy : by > cy ? by : cy;
789
+ const minZ = zOrder(x0, y0, minX, minY, invSize), maxZ = zOrder(x1, y1, minX, minY, invSize);
790
+ let p = ear.prevZ, n = ear.nextZ;
791
+ while (p && p.z >= minZ && n && n.z <= maxZ) {
792
+ if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
793
+ p = p.prevZ;
794
+ if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false;
795
+ n = n.nextZ;
796
+ }
797
+ while (p && p.z >= minZ) {
798
+ if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
799
+ p = p.prevZ;
800
+ }
801
+ while (n && n.z <= maxZ) {
802
+ if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false;
803
+ n = n.nextZ;
804
+ }
805
+ return true;
806
+ }
807
+ function cureLocalIntersections(start, triangles, dim) {
808
+ let p = start;
809
+ do {
810
+ const a = p.prev, b = p.next.next;
811
+ if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) {
812
+ triangles.push(a.i / dim | 0);
813
+ triangles.push(p.i / dim | 0);
814
+ triangles.push(b.i / dim | 0);
815
+ removeNode(p);
816
+ removeNode(p.next);
817
+ p = start = b;
818
+ }
819
+ p = p.next;
820
+ } while (p !== start);
821
+ return filterPoints(p);
822
+ }
823
+ function splitEarcut(start, triangles, dim, minX, minY, invSize) {
824
+ let a = start;
825
+ do {
826
+ let b = a.next.next;
827
+ while (b !== a.prev) {
828
+ if (a.i !== b.i && isValidDiagonal(a, b)) {
829
+ let c = splitPolygon(a, b);
830
+ a = filterPoints(a, a.next);
831
+ c = filterPoints(c, c.next);
832
+ earcutLinked(a, triangles, dim, minX, minY, invSize, 0);
833
+ earcutLinked(c, triangles, dim, minX, minY, invSize, 0);
834
+ return;
835
+ }
836
+ b = b.next;
837
+ }
838
+ a = a.next;
839
+ } while (a !== start);
840
+ }
841
+ function eliminateHoles(data, holeIndices, outerNode, dim) {
842
+ const queue = [];
843
+ let i, len, start, end, list2;
844
+ for (i = 0, len = holeIndices.length; i < len; i++) {
845
+ start = holeIndices[i] * dim;
846
+ end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
847
+ list2 = linkedList(data, start, end, dim, false);
848
+ if (list2 === list2.next) list2.steiner = true;
849
+ queue.push(getLeftmost(list2));
850
+ }
851
+ queue.sort(compareX);
852
+ for (i = 0; i < queue.length; i++) {
853
+ outerNode = eliminateHole(queue[i], outerNode);
854
+ }
855
+ return outerNode;
856
+ }
857
+ function compareX(a, b) {
858
+ return a.x - b.x;
859
+ }
860
+ function eliminateHole(hole, outerNode) {
861
+ const bridge = findHoleBridge(hole, outerNode);
862
+ if (!bridge) {
863
+ return outerNode;
864
+ }
865
+ const bridgeReverse = splitPolygon(bridge, hole);
866
+ filterPoints(bridgeReverse, bridgeReverse.next);
867
+ return filterPoints(bridge, bridge.next);
868
+ }
869
+ function findHoleBridge(hole, outerNode) {
870
+ let p = outerNode, qx = -Infinity, m;
871
+ const hx = hole.x, hy = hole.y;
872
+ do {
873
+ if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) {
874
+ const x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y);
875
+ if (x <= hx && x > qx) {
876
+ qx = x;
877
+ m = p.x < p.next.x ? p : p.next;
878
+ if (x === hx) return m;
879
+ }
880
+ }
881
+ p = p.next;
882
+ } while (p !== outerNode);
883
+ if (!m) return null;
884
+ const stop = m, mx = m.x, my = m.y;
885
+ let tanMin = Infinity, tan;
886
+ p = m;
887
+ do {
888
+ if (hx >= p.x && p.x >= mx && hx !== p.x && pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) {
889
+ tan = Math.abs(hy - p.y) / (hx - p.x);
890
+ if (locallyInside(p, hole) && (tan < tanMin || tan === tanMin && (p.x > m.x || p.x === m.x && sectorContainsSector(m, p)))) {
891
+ m = p;
892
+ tanMin = tan;
893
+ }
894
+ }
895
+ p = p.next;
896
+ } while (p !== stop);
897
+ return m;
898
+ }
899
+ function sectorContainsSector(m, p) {
900
+ return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0;
901
+ }
902
+ function indexCurve(start, minX, minY, invSize) {
903
+ let p = start;
904
+ do {
905
+ if (p.z === 0) p.z = zOrder(p.x, p.y, minX, minY, invSize);
906
+ p.prevZ = p.prev;
907
+ p.nextZ = p.next;
908
+ p = p.next;
909
+ } while (p !== start);
910
+ p.prevZ.nextZ = null;
911
+ p.prevZ = null;
912
+ sortLinked(p);
913
+ }
914
+ function sortLinked(list2) {
915
+ let i, p, q, e, tail, numMerges, pSize, qSize, inSize = 1;
916
+ do {
917
+ p = list2;
918
+ list2 = null;
919
+ tail = null;
920
+ numMerges = 0;
921
+ while (p) {
922
+ numMerges++;
923
+ q = p;
924
+ pSize = 0;
925
+ for (i = 0; i < inSize; i++) {
926
+ pSize++;
927
+ q = q.nextZ;
928
+ if (!q) break;
929
+ }
930
+ qSize = inSize;
931
+ while (pSize > 0 || qSize > 0 && q) {
932
+ if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) {
933
+ e = p;
934
+ p = p.nextZ;
935
+ pSize--;
936
+ } else {
937
+ e = q;
938
+ q = q.nextZ;
939
+ qSize--;
940
+ }
941
+ if (tail) tail.nextZ = e;
942
+ else list2 = e;
943
+ e.prevZ = tail;
944
+ tail = e;
945
+ }
946
+ p = q;
947
+ }
948
+ tail.nextZ = null;
949
+ inSize *= 2;
950
+ } while (numMerges > 1);
951
+ return list2;
952
+ }
953
+ function zOrder(x, y, minX, minY, invSize) {
954
+ x = (x - minX) * invSize | 0;
955
+ y = (y - minY) * invSize | 0;
956
+ x = (x | x << 8) & 16711935;
957
+ x = (x | x << 4) & 252645135;
958
+ x = (x | x << 2) & 858993459;
959
+ x = (x | x << 1) & 1431655765;
960
+ y = (y | y << 8) & 16711935;
961
+ y = (y | y << 4) & 252645135;
962
+ y = (y | y << 2) & 858993459;
963
+ y = (y | y << 1) & 1431655765;
964
+ return x | y << 1;
965
+ }
966
+ function getLeftmost(start) {
967
+ let p = start, leftmost = start;
968
+ do {
969
+ if (p.x < leftmost.x || p.x === leftmost.x && p.y < leftmost.y) leftmost = p;
970
+ p = p.next;
971
+ } while (p !== start);
972
+ return leftmost;
973
+ }
974
+ function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) {
975
+ return (cx - px) * (ay - py) >= (ax - px) * (cy - py) && (ax - px) * (by - py) >= (bx - px) * (ay - py) && (bx - px) * (cy - py) >= (cx - px) * (by - py);
976
+ }
977
+ function isValidDiagonal(a, b) {
978
+ return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && // dones't intersect other edges
979
+ (locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible
980
+ (area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors
981
+ equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0);
982
+ }
983
+ function area(p, q, r) {
984
+ return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
985
+ }
986
+ function equals(p1, p2) {
987
+ return p1.x === p2.x && p1.y === p2.y;
988
+ }
989
+ function intersects(p1, q1, p2, q2) {
990
+ const o1 = sign(area(p1, q1, p2));
991
+ const o2 = sign(area(p1, q1, q2));
992
+ const o3 = sign(area(p2, q2, p1));
993
+ const o4 = sign(area(p2, q2, q1));
994
+ if (o1 !== o2 && o3 !== o4) return true;
995
+ if (o1 === 0 && onSegment(p1, p2, q1)) return true;
996
+ if (o2 === 0 && onSegment(p1, q2, q1)) return true;
997
+ if (o3 === 0 && onSegment(p2, p1, q2)) return true;
998
+ if (o4 === 0 && onSegment(p2, q1, q2)) return true;
999
+ return false;
1000
+ }
1001
+ function onSegment(p, q, r) {
1002
+ return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y);
1003
+ }
1004
+ function sign(num2) {
1005
+ return num2 > 0 ? 1 : num2 < 0 ? -1 : 0;
1006
+ }
1007
+ function intersectsPolygon(a, b) {
1008
+ let p = a;
1009
+ do {
1010
+ if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && intersects(p, p.next, a, b)) return true;
1011
+ p = p.next;
1012
+ } while (p !== a);
1013
+ return false;
1014
+ }
1015
+ function locallyInside(a, b) {
1016
+ return area(a.prev, a, a.next) < 0 ? area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 : area(a, b, a.prev) < 0 || area(a, a.next, b) < 0;
1017
+ }
1018
+ function middleInside(a, b) {
1019
+ let p = a, inside = false;
1020
+ const px = (a.x + b.x) / 2, py = (a.y + b.y) / 2;
1021
+ do {
1022
+ if (p.y > py !== p.next.y > py && p.next.y !== p.y && px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x)
1023
+ inside = !inside;
1024
+ p = p.next;
1025
+ } while (p !== a);
1026
+ return inside;
1027
+ }
1028
+ function splitPolygon(a, b) {
1029
+ const a2 = new Node(a.i, a.x, a.y), b2 = new Node(b.i, b.x, b.y), an = a.next, bp = b.prev;
1030
+ a.next = b;
1031
+ b.prev = a;
1032
+ a2.next = an;
1033
+ an.prev = a2;
1034
+ b2.next = a2;
1035
+ a2.prev = b2;
1036
+ bp.next = b2;
1037
+ b2.prev = bp;
1038
+ return b2;
1039
+ }
1040
+ function insertNode(i, x, y, last) {
1041
+ const p = new Node(i, x, y);
1042
+ if (!last) {
1043
+ p.prev = p;
1044
+ p.next = p;
1045
+ } else {
1046
+ p.next = last.next;
1047
+ p.prev = last;
1048
+ last.next.prev = p;
1049
+ last.next = p;
1050
+ }
1051
+ return p;
1052
+ }
1053
+ function removeNode(p) {
1054
+ p.next.prev = p.prev;
1055
+ p.prev.next = p.next;
1056
+ if (p.prevZ) p.prevZ.nextZ = p.nextZ;
1057
+ if (p.nextZ) p.nextZ.prevZ = p.prevZ;
1058
+ }
1059
+ function Node(i, x, y) {
1060
+ this.i = i;
1061
+ this.x = x;
1062
+ this.y = y;
1063
+ this.prev = null;
1064
+ this.next = null;
1065
+ this.z = 0;
1066
+ this.prevZ = null;
1067
+ this.nextZ = null;
1068
+ this.steiner = false;
1069
+ }
1070
+ function signedArea(data, start, end, dim) {
1071
+ let sum = 0;
1072
+ for (let i = start, j = end - dim; i < end; i += dim) {
1073
+ sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]);
1074
+ j = i;
1075
+ }
1076
+ return sum;
1077
+ }
1078
+
1079
+ // src/primitives/extrude.ts
1080
+ var equals2 = (a, b) => a.x === b.x && a.y === b.y;
1081
+ var linePoint = (v1, v2, t) => t === 1 ? { x: v2.x, y: v2.y } : { x: (v2.x - v1.x) * t + v1.x, y: (v2.y - v1.y) * t + v1.y };
1082
+ function pathPoints(points) {
1083
+ const out = [];
1084
+ let last;
1085
+ for (let i = 1; i < points.length; i++) {
1086
+ const v1 = { x: points[i - 1][0], y: points[i - 1][1] };
1087
+ const v2 = { x: points[i][0], y: points[i][1] };
1088
+ for (let d = 0; d <= 1; d++) {
1089
+ const point = linePoint(v1, v2, d / 1);
1090
+ if (last && equals2(last, point)) continue;
1091
+ out.push(point);
1092
+ last = point;
1093
+ }
1094
+ }
1095
+ return out;
1096
+ }
1097
+ function area2(contour) {
1098
+ const n = contour.length;
1099
+ let a = 0;
1100
+ for (let p = n - 1, q = 0; q < n; p = q++) a += contour[p].x * contour[q].y - contour[q].x * contour[p].y;
1101
+ return a * 0.5;
1102
+ }
1103
+ var isClockWise = (pts) => area2(pts) < 0;
1104
+ function removeDupEndPts(points) {
1105
+ const l = points.length;
1106
+ if (l > 2 && equals2(points[l - 1], points[0])) points.pop();
1107
+ }
1108
+ function triangulateShape(contour, holes) {
1109
+ const vertices = [];
1110
+ const holeIndices = [];
1111
+ const faces = [];
1112
+ removeDupEndPts(contour);
1113
+ for (const p of contour) vertices.push(p.x, p.y);
1114
+ let holeIndex = contour.length;
1115
+ holes.forEach(removeDupEndPts);
1116
+ for (let i = 0; i < holes.length; i++) {
1117
+ holeIndices.push(holeIndex);
1118
+ holeIndex += holes[i].length;
1119
+ for (const p of holes[i]) vertices.push(p.x, p.y);
1120
+ }
1121
+ const triangles = Earcut.triangulate(vertices, holeIndices);
1122
+ for (let i = 0; i < triangles.length; i += 3) faces.push(triangles.slice(i, i + 3));
1123
+ return faces;
1124
+ }
1125
+ function computeVertexNormals(positions, indices) {
1126
+ const normals = new Float32Array(positions.length);
1127
+ const get = (array, i) => [array[i * 3], array[i * 3 + 1], array[i * 3 + 2]];
1128
+ const face = (vA, vB, vC) => {
1129
+ const pA = get(positions, vA);
1130
+ const pB = get(positions, vB);
1131
+ const pC = get(positions, vC);
1132
+ const cb = [pC[0] - pB[0], pC[1] - pB[1], pC[2] - pB[2]];
1133
+ const ab = [pA[0] - pB[0], pA[1] - pB[1], pA[2] - pB[2]];
1134
+ return [cb[1] * ab[2] - cb[2] * ab[1], cb[2] * ab[0] - cb[0] * ab[2], cb[0] * ab[1] - cb[1] * ab[0]];
1135
+ };
1136
+ const set = (i, x, y, z) => {
1137
+ normals[i * 3] = x;
1138
+ normals[i * 3 + 1] = y;
1139
+ normals[i * 3 + 2] = z;
1140
+ };
1141
+ if (indices) {
1142
+ for (let i = 0; i < indices.length; i += 3) {
1143
+ const vA = indices[i];
1144
+ const vB = indices[i + 1];
1145
+ const vC = indices[i + 2];
1146
+ const cb = face(vA, vB, vC);
1147
+ for (const v of [vA, vB, vC]) {
1148
+ const n = get(normals, v);
1149
+ set(v, n[0] + cb[0], n[1] + cb[1], n[2] + cb[2]);
1150
+ }
1151
+ }
1152
+ } else {
1153
+ for (let i = 0; i < positions.length / 3; i += 3) {
1154
+ const cb = face(i, i + 1, i + 2);
1155
+ set(i, cb[0], cb[1], cb[2]);
1156
+ set(i + 1, cb[0], cb[1], cb[2]);
1157
+ set(i + 2, cb[0], cb[1], cb[2]);
1158
+ }
1159
+ }
1160
+ for (let i = 0; i < normals.length / 3; i++) {
1161
+ const [x, y, z] = get(normals, i);
1162
+ const s = 1 / (Math.sqrt(x * x + y * y + z * z) || 1);
1163
+ set(i, x * s, y * s, z * s);
1164
+ }
1165
+ return normals;
1166
+ }
1167
+ function extrude(outline, depth = 1, steps = 1, bevel = void 0) {
1168
+ const parameters = { shape: outline, depth, steps, bevel: bevel ?? {} };
1169
+ const verticesArray = [];
1170
+ const uvArray = [];
1171
+ const groups = [];
1172
+ const placeholder = [];
1173
+ const bevelEnabled = bevel !== false;
1174
+ const spec = bevel === false || bevel === void 0 ? {} : bevel;
1175
+ let bevelThickness = spec.thickness !== void 0 ? spec.thickness : 0.2;
1176
+ let bevelSize = spec.size !== void 0 ? spec.size : bevelThickness - 0.1;
1177
+ let bevelOffset = spec.offset !== void 0 ? spec.offset : 0;
1178
+ let bevelSegments = spec.segments !== void 0 ? spec.segments : 3;
1179
+ if (!bevelEnabled) {
1180
+ bevelSegments = 0;
1181
+ bevelThickness = 0;
1182
+ bevelSize = 0;
1183
+ bevelOffset = 0;
1184
+ }
1185
+ let vertices = pathPoints(outline.outer);
1186
+ const holes = (outline.holes ?? []).map(pathPoints);
1187
+ const reverse = !isClockWise(vertices);
1188
+ if (reverse) {
1189
+ vertices = vertices.reverse();
1190
+ for (let h = 0; h < holes.length; h++) {
1191
+ const ahole = holes[h];
1192
+ if (isClockWise(ahole)) holes[h] = ahole.reverse();
1193
+ }
1194
+ }
1195
+ const faces = triangulateShape(vertices, holes);
1196
+ const contour = vertices;
1197
+ for (const ahole of holes) vertices = vertices.concat(ahole);
1198
+ const scalePt2 = (pt, vec, size) => ({ x: pt.x + vec.x * size, y: pt.y + vec.y * size });
1199
+ const vlen = vertices.length;
1200
+ const flen = faces.length;
1201
+ const getBevelVec = (inPt, inPrev, inNext) => {
1202
+ let vTransX;
1203
+ let vTransY;
1204
+ let shrinkBy;
1205
+ const vPrevX = inPt.x - inPrev.x;
1206
+ const vPrevY = inPt.y - inPrev.y;
1207
+ const vNextX = inNext.x - inPt.x;
1208
+ const vNextY = inNext.y - inPt.y;
1209
+ const vPrevLensq = vPrevX * vPrevX + vPrevY * vPrevY;
1210
+ const collinear0 = vPrevX * vNextY - vPrevY * vNextX;
1211
+ if (Math.abs(collinear0) > Number.EPSILON) {
1212
+ const vPrevLen = Math.sqrt(vPrevLensq);
1213
+ const vNextLen = Math.sqrt(vNextX * vNextX + vNextY * vNextY);
1214
+ const ptPrevShiftX = inPrev.x - vPrevY / vPrevLen;
1215
+ const ptPrevShiftY = inPrev.y + vPrevX / vPrevLen;
1216
+ const ptNextShiftX = inNext.x - vNextY / vNextLen;
1217
+ const ptNextShiftY = inNext.y + vNextX / vNextLen;
1218
+ const sf = ((ptNextShiftX - ptPrevShiftX) * vNextY - (ptNextShiftY - ptPrevShiftY) * vNextX) / (vPrevX * vNextY - vPrevY * vNextX);
1219
+ vTransX = ptPrevShiftX + vPrevX * sf - inPt.x;
1220
+ vTransY = ptPrevShiftY + vPrevY * sf - inPt.y;
1221
+ const vTransLensq = vTransX * vTransX + vTransY * vTransY;
1222
+ if (vTransLensq <= 2) return { x: vTransX, y: vTransY };
1223
+ shrinkBy = Math.sqrt(vTransLensq / 2);
1224
+ } else {
1225
+ let directionEq = false;
1226
+ if (vPrevX > Number.EPSILON) {
1227
+ if (vNextX > Number.EPSILON) directionEq = true;
1228
+ } else if (vPrevX < -Number.EPSILON) {
1229
+ if (vNextX < -Number.EPSILON) directionEq = true;
1230
+ } else if (Math.sign(vPrevY) === Math.sign(vNextY)) {
1231
+ directionEq = true;
1232
+ }
1233
+ if (directionEq) {
1234
+ vTransX = -vPrevY;
1235
+ vTransY = vPrevX;
1236
+ shrinkBy = Math.sqrt(vPrevLensq);
1237
+ } else {
1238
+ vTransX = vPrevX;
1239
+ vTransY = vPrevY;
1240
+ shrinkBy = Math.sqrt(vPrevLensq / 2);
1241
+ }
1242
+ }
1243
+ return { x: vTransX / shrinkBy, y: vTransY / shrinkBy };
1244
+ };
1245
+ const movements = (ring2) => {
1246
+ const out = [];
1247
+ for (let i = 0, il = ring2.length, j = il - 1, k = i + 1; i < il; i++, j++, k++) {
1248
+ if (j === il) j = 0;
1249
+ if (k === il) k = 0;
1250
+ out[i] = getBevelVec(ring2[i], ring2[j], ring2[k]);
1251
+ }
1252
+ return out;
1253
+ };
1254
+ const contourMovements = movements(contour);
1255
+ const holesMovements = [];
1256
+ let verticesMovements = contourMovements.concat();
1257
+ for (const ahole of holes) {
1258
+ const oneHoleMovements = movements(ahole);
1259
+ holesMovements.push(oneHoleMovements);
1260
+ verticesMovements = verticesMovements.concat(oneHoleMovements);
1261
+ }
1262
+ const v = (x, y, z) => {
1263
+ placeholder.push(x);
1264
+ placeholder.push(y);
1265
+ placeholder.push(z);
1266
+ };
1267
+ const bevelLayer = (b, zOf) => {
1268
+ const t = b / bevelSegments;
1269
+ const z = bevelThickness * Math.cos(t * Math.PI / 2);
1270
+ const bs2 = bevelSize * Math.sin(t * Math.PI / 2) + bevelOffset;
1271
+ for (let i = 0; i < contour.length; i++) {
1272
+ const vert = scalePt2(contour[i], contourMovements[i], bs2);
1273
+ v(vert.x, vert.y, zOf(z));
1274
+ }
1275
+ for (let h = 0; h < holes.length; h++) {
1276
+ const ahole = holes[h];
1277
+ const oneHoleMovements = holesMovements[h];
1278
+ for (let i = 0; i < ahole.length; i++) {
1279
+ const vert = scalePt2(ahole[i], oneHoleMovements[i], bs2);
1280
+ v(vert.x, vert.y, zOf(z));
1281
+ }
1282
+ }
1283
+ };
1284
+ for (let b = 0; b < bevelSegments; b++) bevelLayer(b, (z) => -z);
1285
+ const bs = bevelSize + bevelOffset;
1286
+ for (let i = 0; i < vlen; i++) {
1287
+ const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i];
1288
+ v(vert.x, vert.y, 0);
1289
+ }
1290
+ for (let s = 1; s <= steps; s++) {
1291
+ for (let i = 0; i < vlen; i++) {
1292
+ const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i];
1293
+ v(vert.x, vert.y, depth / steps * s);
1294
+ }
1295
+ }
1296
+ for (let b = bevelSegments - 1; b >= 0; b--) bevelLayer(b, (z) => depth + z);
1297
+ const addVertex = (index) => {
1298
+ verticesArray.push(placeholder[index * 3 + 0]);
1299
+ verticesArray.push(placeholder[index * 3 + 1]);
1300
+ verticesArray.push(placeholder[index * 3 + 2]);
1301
+ };
1302
+ const addUV = (x, y) => {
1303
+ uvArray.push(x);
1304
+ uvArray.push(y);
1305
+ };
1306
+ const at = (index, axis) => verticesArray[index * 3 + axis];
1307
+ const f3 = (a, b, c) => {
1308
+ addVertex(a);
1309
+ addVertex(b);
1310
+ addVertex(c);
1311
+ const nextIndex = verticesArray.length / 3;
1312
+ for (const index of [nextIndex - 3, nextIndex - 2, nextIndex - 1]) addUV(at(index, 0), at(index, 1));
1313
+ };
1314
+ const f4 = (a, b, c, d) => {
1315
+ addVertex(a);
1316
+ addVertex(b);
1317
+ addVertex(d);
1318
+ addVertex(b);
1319
+ addVertex(c);
1320
+ addVertex(d);
1321
+ const nextIndex = verticesArray.length / 3;
1322
+ const iA = nextIndex - 6;
1323
+ const iB = nextIndex - 3;
1324
+ const iC = nextIndex - 2;
1325
+ const iD = nextIndex - 1;
1326
+ const byX = Math.abs(at(iA, 1) - at(iB, 1)) < Math.abs(at(iA, 0) - at(iB, 0));
1327
+ const uv = (index) => [byX ? at(index, 0) : at(index, 1), 1 - at(index, 2)];
1328
+ const uvs = [uv(iA), uv(iB), uv(iC), uv(iD)];
1329
+ for (const k of [0, 1, 3, 1, 2, 3]) addUV(...uvs[k]);
1330
+ };
1331
+ let start = verticesArray.length / 3;
1332
+ if (bevelEnabled) {
1333
+ let offset = 0;
1334
+ for (let i = 0; i < flen; i++) {
1335
+ const face = faces[i];
1336
+ f3(face[2] + offset, face[1] + offset, face[0] + offset);
1337
+ }
1338
+ const layer = steps + bevelSegments * 2;
1339
+ offset = vlen * layer;
1340
+ for (let i = 0; i < flen; i++) {
1341
+ const face = faces[i];
1342
+ f3(face[0] + offset, face[1] + offset, face[2] + offset);
1343
+ }
1344
+ } else {
1345
+ for (let i = 0; i < flen; i++) {
1346
+ const face = faces[i];
1347
+ f3(face[2], face[1], face[0]);
1348
+ }
1349
+ for (let i = 0; i < flen; i++) {
1350
+ const face = faces[i];
1351
+ f3(face[0] + vlen * steps, face[1] + vlen * steps, face[2] + vlen * steps);
1352
+ }
1353
+ }
1354
+ groups.push({ start, count: verticesArray.length / 3 - start, materialIndex: 0 });
1355
+ start = verticesArray.length / 3;
1356
+ const sidewalls = (ring2, layeroffset2) => {
1357
+ let i = ring2.length;
1358
+ while (--i >= 0) {
1359
+ const j = i;
1360
+ let k = i - 1;
1361
+ if (k < 0) k = ring2.length - 1;
1362
+ for (let s = 0, sl = steps + bevelSegments * 2; s < sl; s++) {
1363
+ const slen1 = vlen * s;
1364
+ const slen2 = vlen * (s + 1);
1365
+ f4(layeroffset2 + j + slen1, layeroffset2 + k + slen1, layeroffset2 + k + slen2, layeroffset2 + j + slen2);
1366
+ }
1367
+ }
1368
+ };
1369
+ let layeroffset = 0;
1370
+ sidewalls(contour, layeroffset);
1371
+ layeroffset += contour.length;
1372
+ for (const ahole of holes) {
1373
+ sidewalls(ahole, layeroffset);
1374
+ layeroffset += ahole.length;
1375
+ }
1376
+ groups.push({ start, count: verticesArray.length / 3 - start, materialIndex: 1 });
1377
+ const positions = new Float32Array(verticesArray);
1378
+ return { type: "ExtrudeGeometry", parameters, positions, normals: computeVertexNormals(positions), uvs: new Float32Array(uvArray), groups };
1379
+ }
1380
+
1381
+ // src/primitives/index.ts
1382
+ var PARAMETER_KEYS = {
1383
+ box: ["size", "segments"],
1384
+ cylinder: ["radiusTop", "radiusBottom", "height", "radialSegments", "heightSegments", "openEnded", "thetaStart", "thetaLength"],
1385
+ cone: ["radius", "height", "radialSegments", "heightSegments", "openEnded", "thetaStart", "thetaLength"],
1386
+ sphere: ["radius", "widthSegments", "heightSegments", "phiStart", "phiLength", "thetaStart", "thetaLength"],
1387
+ plane: ["width", "height", "widthSegments", "heightSegments"],
1388
+ circle: ["radius", "segments", "thetaStart", "thetaLength"],
1389
+ ring: ["innerRadius", "outerRadius", "thetaSegments", "phiSegments", "thetaStart", "thetaLength"],
1390
+ lathe: ["points", "segments", "phiStart", "phiLength"],
1391
+ torus: ["radius", "tube", "radialSegments", "tubularSegments", "arc"],
1392
+ extrude: ["shape", "depth", "steps", "bevel"],
1393
+ difference: ["size", "subtract"],
1394
+ mesh: ["positions", "normals", "uvs", "indices"]
1395
+ };
1396
+ var SEGMENT_KEYS = {
1397
+ cylinder: ["radialSegments"],
1398
+ cone: ["radialSegments"],
1399
+ sphere: ["widthSegments", "heightSegments"],
1400
+ circle: ["segments"],
1401
+ ring: ["thetaSegments"],
1402
+ lathe: ["segments"],
1403
+ torus: ["radialSegments", "tubularSegments"]
1404
+ };
1405
+ var SEGMENT_DEFAULTS = { radialSegments: 32, widthSegments: 32, heightSegments: 16, segments: 12, thetaSegments: 32, tubularSegments: 48 };
1406
+ var segmentDefault = (kind, key) => kind === "circle" && key === "segments" ? 32 : kind === "torus" && key === "radialSegments" ? 12 : SEGMENT_DEFAULTS[key];
1407
+ function primitiveParameters(node, options = {}) {
1408
+ const out = {};
1409
+ const source = node;
1410
+ for (const key of PARAMETER_KEYS[node.kind]) if (source[key] !== void 0) out[key] = source[key];
1411
+ const detail = options.detail ?? 1;
1412
+ if (detail !== 1) {
1413
+ const floor = options.minSegments ?? 3;
1414
+ for (const key of SEGMENT_KEYS[node.kind] ?? []) {
1415
+ const given = out[key] ?? segmentDefault(node.kind, key);
1416
+ out[key] = Math.max(floor, Math.round(given * detail));
1417
+ }
1418
+ }
1419
+ return out;
1420
+ }
1421
+ function mesh(p) {
1422
+ const positions = new Float32Array(p.positions);
1423
+ const indices = p.indices ? indexArray(p.indices) : void 0;
1424
+ return {
1425
+ type: "BufferGeometry",
1426
+ parameters: {},
1427
+ positions,
1428
+ normals: p.normals ? new Float32Array(p.normals) : computeVertexNormals(positions, indices),
1429
+ uvs: p.uvs ? new Float32Array(p.uvs) : new Float32Array(positions.length / 3 * 2),
1430
+ ...indices ? { indices } : {},
1431
+ groups: []
1432
+ };
1433
+ }
1434
+ function make(kind, p) {
1435
+ switch (kind) {
1436
+ case "box": {
1437
+ const size = p.size ?? [1, 1, 1];
1438
+ const segments = p.segments ?? [1, 1, 1];
1439
+ return box(size[0], size[1], size[2], segments[0], segments[1], segments[2]);
1440
+ }
1441
+ case "cylinder":
1442
+ return cylinder(p.radiusTop, p.radiusBottom, p.height, p.radialSegments, p.heightSegments, p.openEnded, p.thetaStart, p.thetaLength);
1443
+ case "cone":
1444
+ return cone(p.radius, p.height, p.radialSegments, p.heightSegments, p.openEnded, p.thetaStart, p.thetaLength);
1445
+ case "sphere":
1446
+ return sphere(p.radius, p.widthSegments, p.heightSegments, p.phiStart, p.phiLength, p.thetaStart, p.thetaLength);
1447
+ case "plane":
1448
+ return plane(p.width, p.height, p.widthSegments, p.heightSegments);
1449
+ case "circle":
1450
+ return circle(p.radius, p.segments, p.thetaStart, p.thetaLength);
1451
+ case "ring":
1452
+ return ring(p.innerRadius, p.outerRadius, p.thetaSegments, p.phiSegments, p.thetaStart, p.thetaLength);
1453
+ case "lathe":
1454
+ return lathe(p.points, p.segments, p.phiStart, p.phiLength);
1455
+ case "torus":
1456
+ return torus(p.radius, p.tube, p.radialSegments, p.tubularSegments, p.arc);
1457
+ case "extrude":
1458
+ return extrude(p.shape, p.depth, p.steps, p.bevel);
1459
+ case "difference":
1460
+ return difference(p.size, p.subtract ?? []);
1461
+ case "mesh":
1462
+ return mesh(p);
1463
+ default:
1464
+ throw new Error(`game-shapes: unknown primitive kind "${String(kind)}"`);
1465
+ }
1466
+ }
1467
+ var templates = /* @__PURE__ */ new Map();
1468
+ var templateLimit = 4096;
1469
+ var stats = { hits: 0, misses: 0 };
1470
+ function setTemplateCacheSize(size) {
1471
+ templateLimit = Math.max(0, Math.floor(size));
1472
+ while (templates.size > templateLimit) templates.delete(templates.keys().next().value);
1473
+ }
1474
+ function clearTemplateCache() {
1475
+ templates.clear();
1476
+ stats.hits = 0;
1477
+ stats.misses = 0;
1478
+ }
1479
+ var templateCacheStats = () => ({ size: templates.size, ...stats });
1480
+ function buildPrimitive(node, options = {}) {
1481
+ const parameters = primitiveParameters(node, options);
1482
+ if (node.kind === "mesh" || templateLimit === 0) return make(node.kind, parameters);
1483
+ const key = `${node.kind}:${canonicalJson(parameters)}`;
1484
+ const cached = templates.get(key);
1485
+ if (cached) {
1486
+ stats.hits++;
1487
+ templates.delete(key);
1488
+ templates.set(key, cached);
1489
+ return cached;
1490
+ }
1491
+ stats.misses++;
1492
+ const built = make(node.kind, parameters);
1493
+ templates.set(key, built);
1494
+ if (templates.size > templateLimit) templates.delete(templates.keys().next().value);
1495
+ return built;
1496
+ }
1497
+ function clonePrimitive(p) {
1498
+ return {
1499
+ type: p.type,
1500
+ parameters: p.parameters,
1501
+ positions: p.positions.slice(),
1502
+ normals: p.normals.slice(),
1503
+ uvs: p.uvs.slice(),
1504
+ ...p.indices ? { indices: p.indices.slice() } : {},
1505
+ groups: p.groups.map((group) => ({ ...group }))
1506
+ };
1507
+ }
1508
+
1509
+ // src/random.ts
1510
+ function mulberry32(seed) {
1511
+ let state = seed >>> 0;
1512
+ return () => {
1513
+ state = state + 1831565813 >>> 0;
1514
+ let t = state;
1515
+ t = Math.imul(t ^ t >>> 15, t | 1);
1516
+ t ^= t + Math.imul(t ^ t >>> 7, t | 61);
1517
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
1518
+ };
1519
+ }
1520
+ var streamFor = (seed, generator, path) => mulberry32(parseInt(hash64(`${String(seed)}\0${generator}\0${path}`).slice(0, 8), 16));
1521
+
1522
+ // src/registry.ts
1523
+ var generators = /* @__PURE__ */ new Map();
1524
+ function registerGenerator(name, generator) {
1525
+ if (!/^[a-zA-Z][\w.:-]*$/.test(name)) throw new Error(`game-shapes: generator name "${name}" must start with a letter and use letters, digits, ".", ":", "-" or "_"`);
1526
+ generators.set(name, generator);
1527
+ }
1528
+ var unregisterGenerator = (name) => generators.delete(name);
1529
+ var getGenerator = (name) => generators.get(name);
1530
+ var listGenerators = () => [...generators.keys()].sort();
1531
+
1532
+ // schema/shape.schema.json
1533
+ var shape_schema_default = {
1534
+ $schema: "https://json-schema.org/draft/2020-12/schema",
1535
+ $id: "https://github.com/Andersonfrfilho/adatechnology-game-kit/packages/game-shapes/schema/shape.schema.json",
1536
+ title: "game-shapes shape",
1537
+ type: "object",
1538
+ required: [
1539
+ "version",
1540
+ "root"
1541
+ ],
1542
+ additionalProperties: false,
1543
+ properties: {
1544
+ version: {
1545
+ const: 1
1546
+ },
1547
+ name: {
1548
+ type: "string"
1549
+ },
1550
+ materials: {
1551
+ type: "object",
1552
+ additionalProperties: {
1553
+ $ref: "#/$defs/material"
1554
+ }
1555
+ },
1556
+ root: {
1557
+ $ref: "#/$defs/node"
1558
+ },
1559
+ lod: {
1560
+ type: "array",
1561
+ items: {
1562
+ $ref: "#/$defs/lodLevel"
1563
+ }
1564
+ },
1565
+ cullDistance: {
1566
+ type: "number",
1567
+ exclusiveMinimum: 0
1568
+ }
1569
+ },
1570
+ $defs: {
1571
+ number: {
1572
+ type: "number"
1573
+ },
1574
+ count: {
1575
+ type: "integer",
1576
+ minimum: 0
1577
+ },
1578
+ vec2: {
1579
+ type: "array",
1580
+ items: {
1581
+ type: "number"
1582
+ },
1583
+ minItems: 2,
1584
+ maxItems: 2
1585
+ },
1586
+ vec3: {
1587
+ type: "array",
1588
+ items: {
1589
+ type: "number"
1590
+ },
1591
+ minItems: 3,
1592
+ maxItems: 3
1593
+ },
1594
+ mat4: {
1595
+ type: "array",
1596
+ items: {
1597
+ type: "number"
1598
+ },
1599
+ minItems: 16,
1600
+ maxItems: 16
1601
+ },
1602
+ color: {
1603
+ oneOf: [
1604
+ {
1605
+ type: "integer",
1606
+ minimum: 0,
1607
+ maximum: 16777215
1608
+ },
1609
+ {
1610
+ type: "string",
1611
+ pattern: "^#[0-9a-fA-F]{6}$"
1612
+ }
1613
+ ]
1614
+ },
1615
+ uvRect: {
1616
+ type: "object",
1617
+ additionalProperties: false,
1618
+ required: [
1619
+ "u0",
1620
+ "v0",
1621
+ "u1",
1622
+ "v1"
1623
+ ],
1624
+ properties: {
1625
+ u0: {
1626
+ type: "number"
1627
+ },
1628
+ v0: {
1629
+ type: "number"
1630
+ },
1631
+ u1: {
1632
+ type: "number"
1633
+ },
1634
+ v1: {
1635
+ type: "number"
1636
+ }
1637
+ }
1638
+ },
1639
+ rotationOrder: {
1640
+ enum: [
1641
+ "XYZ",
1642
+ "YXZ",
1643
+ "ZXY",
1644
+ "ZYX",
1645
+ "YZX",
1646
+ "XZY"
1647
+ ]
1648
+ },
1649
+ transform: {
1650
+ type: "object",
1651
+ properties: {
1652
+ position: {
1653
+ $ref: "#/$defs/vec3"
1654
+ },
1655
+ rotation: {
1656
+ $ref: "#/$defs/vec3"
1657
+ },
1658
+ rotationOrder: {
1659
+ $ref: "#/$defs/rotationOrder"
1660
+ },
1661
+ scale: {
1662
+ oneOf: [
1663
+ {
1664
+ type: "number"
1665
+ },
1666
+ {
1667
+ $ref: "#/$defs/vec3"
1668
+ }
1669
+ ]
1670
+ },
1671
+ heading: {
1672
+ type: "number"
1673
+ },
1674
+ matrix: {
1675
+ $ref: "#/$defs/mat4"
1676
+ }
1677
+ }
1678
+ },
1679
+ instance: {
1680
+ allOf: [
1681
+ {
1682
+ $ref: "#/$defs/transform"
1683
+ }
1684
+ ],
1685
+ properties: {
1686
+ position: true,
1687
+ rotation: true,
1688
+ rotationOrder: true,
1689
+ scale: true,
1690
+ heading: true,
1691
+ matrix: true,
1692
+ color: {
1693
+ $ref: "#/$defs/color"
1694
+ }
1695
+ },
1696
+ unevaluatedProperties: false
1697
+ },
1698
+ material: {
1699
+ type: "object",
1700
+ additionalProperties: false,
1701
+ properties: {
1702
+ color: {
1703
+ $ref: "#/$defs/color"
1704
+ },
1705
+ roughness: {
1706
+ type: "number",
1707
+ minimum: 0,
1708
+ maximum: 1
1709
+ },
1710
+ metalness: {
1711
+ type: "number",
1712
+ minimum: 0,
1713
+ maximum: 1
1714
+ },
1715
+ opacity: {
1716
+ type: "number",
1717
+ minimum: 0,
1718
+ maximum: 1
1719
+ },
1720
+ transparent: {
1721
+ type: "boolean"
1722
+ },
1723
+ emissive: {
1724
+ $ref: "#/$defs/color"
1725
+ },
1726
+ emissiveIntensity: {
1727
+ type: "number",
1728
+ minimum: 0
1729
+ },
1730
+ side: {
1731
+ enum: [
1732
+ "front",
1733
+ "back",
1734
+ "double"
1735
+ ]
1736
+ },
1737
+ flatShading: {
1738
+ type: "boolean"
1739
+ },
1740
+ vertexColors: {
1741
+ type: "boolean"
1742
+ },
1743
+ depthWrite: {
1744
+ type: "boolean"
1745
+ }
1746
+ }
1747
+ },
1748
+ lodLevel: {
1749
+ type: "object",
1750
+ additionalProperties: false,
1751
+ required: [
1752
+ "distance",
1753
+ "detail"
1754
+ ],
1755
+ properties: {
1756
+ distance: {
1757
+ type: "number",
1758
+ minimum: 0
1759
+ },
1760
+ detail: {
1761
+ type: "number",
1762
+ exclusiveMinimum: 0,
1763
+ maximum: 1
1764
+ },
1765
+ minSize: {
1766
+ type: "number",
1767
+ minimum: 0
1768
+ },
1769
+ minSegments: {
1770
+ type: "integer",
1771
+ minimum: 3
1772
+ }
1773
+ }
1774
+ },
1775
+ common: {
1776
+ allOf: [
1777
+ {
1778
+ $ref: "#/$defs/transform"
1779
+ }
1780
+ ],
1781
+ properties: {
1782
+ kind: {
1783
+ type: "string"
1784
+ },
1785
+ name: {
1786
+ type: "string"
1787
+ },
1788
+ part: {
1789
+ type: "string"
1790
+ },
1791
+ material: {
1792
+ type: "string"
1793
+ },
1794
+ color: {
1795
+ $ref: "#/$defs/color"
1796
+ },
1797
+ uv: {
1798
+ $ref: "#/$defs/uvRect"
1799
+ },
1800
+ instances: {
1801
+ type: "array",
1802
+ items: {
1803
+ $ref: "#/$defs/instance"
1804
+ }
1805
+ },
1806
+ minDetail: {
1807
+ type: "number",
1808
+ exclusiveMinimum: 0,
1809
+ maximum: 1
1810
+ },
1811
+ tags: {
1812
+ type: "array",
1813
+ items: {
1814
+ type: "string"
1815
+ }
1816
+ }
1817
+ }
1818
+ },
1819
+ node: {
1820
+ type: "object",
1821
+ required: [
1822
+ "kind"
1823
+ ],
1824
+ allOf: [
1825
+ {
1826
+ $ref: "#/$defs/common"
1827
+ }
1828
+ ],
1829
+ oneOf: [
1830
+ {
1831
+ properties: {
1832
+ kind: {
1833
+ const: "group"
1834
+ },
1835
+ children: {
1836
+ type: "array",
1837
+ items: {
1838
+ $ref: "#/$defs/node"
1839
+ }
1840
+ }
1841
+ }
1842
+ },
1843
+ {
1844
+ properties: {
1845
+ kind: {
1846
+ const: "box"
1847
+ },
1848
+ size: {
1849
+ $ref: "#/$defs/vec3"
1850
+ },
1851
+ segments: {
1852
+ type: "array",
1853
+ items: {
1854
+ type: "integer",
1855
+ minimum: 1
1856
+ },
1857
+ minItems: 3,
1858
+ maxItems: 3
1859
+ }
1860
+ }
1861
+ },
1862
+ {
1863
+ properties: {
1864
+ kind: {
1865
+ const: "cylinder"
1866
+ },
1867
+ radiusTop: {
1868
+ type: "number"
1869
+ },
1870
+ radiusBottom: {
1871
+ type: "number"
1872
+ },
1873
+ height: {
1874
+ type: "number"
1875
+ },
1876
+ radialSegments: {
1877
+ type: "integer",
1878
+ minimum: 1
1879
+ },
1880
+ heightSegments: {
1881
+ type: "integer",
1882
+ minimum: 1
1883
+ },
1884
+ openEnded: {
1885
+ type: "boolean"
1886
+ },
1887
+ thetaStart: {
1888
+ type: "number"
1889
+ },
1890
+ thetaLength: {
1891
+ type: "number"
1892
+ }
1893
+ }
1894
+ },
1895
+ {
1896
+ properties: {
1897
+ kind: {
1898
+ const: "cone"
1899
+ },
1900
+ radius: {
1901
+ type: "number"
1902
+ },
1903
+ height: {
1904
+ type: "number"
1905
+ },
1906
+ radialSegments: {
1907
+ type: "integer",
1908
+ minimum: 1
1909
+ },
1910
+ heightSegments: {
1911
+ type: "integer",
1912
+ minimum: 1
1913
+ },
1914
+ openEnded: {
1915
+ type: "boolean"
1916
+ },
1917
+ thetaStart: {
1918
+ type: "number"
1919
+ },
1920
+ thetaLength: {
1921
+ type: "number"
1922
+ }
1923
+ }
1924
+ },
1925
+ {
1926
+ properties: {
1927
+ kind: {
1928
+ const: "sphere"
1929
+ },
1930
+ radius: {
1931
+ type: "number"
1932
+ },
1933
+ widthSegments: {
1934
+ type: "integer",
1935
+ minimum: 3
1936
+ },
1937
+ heightSegments: {
1938
+ type: "integer",
1939
+ minimum: 2
1940
+ },
1941
+ phiStart: {
1942
+ type: "number"
1943
+ },
1944
+ phiLength: {
1945
+ type: "number"
1946
+ },
1947
+ thetaStart: {
1948
+ type: "number"
1949
+ },
1950
+ thetaLength: {
1951
+ type: "number"
1952
+ }
1953
+ }
1954
+ },
1955
+ {
1956
+ properties: {
1957
+ kind: {
1958
+ const: "plane"
1959
+ },
1960
+ width: {
1961
+ type: "number"
1962
+ },
1963
+ height: {
1964
+ type: "number"
1965
+ },
1966
+ widthSegments: {
1967
+ type: "integer",
1968
+ minimum: 1
1969
+ },
1970
+ heightSegments: {
1971
+ type: "integer",
1972
+ minimum: 1
1973
+ }
1974
+ }
1975
+ },
1976
+ {
1977
+ properties: {
1978
+ kind: {
1979
+ const: "circle"
1980
+ },
1981
+ radius: {
1982
+ type: "number"
1983
+ },
1984
+ segments: {
1985
+ type: "integer",
1986
+ minimum: 3
1987
+ },
1988
+ thetaStart: {
1989
+ type: "number"
1990
+ },
1991
+ thetaLength: {
1992
+ type: "number"
1993
+ }
1994
+ }
1995
+ },
1996
+ {
1997
+ properties: {
1998
+ kind: {
1999
+ const: "ring"
2000
+ },
2001
+ innerRadius: {
2002
+ type: "number"
2003
+ },
2004
+ outerRadius: {
2005
+ type: "number"
2006
+ },
2007
+ thetaSegments: {
2008
+ type: "integer",
2009
+ minimum: 3
2010
+ },
2011
+ phiSegments: {
2012
+ type: "integer",
2013
+ minimum: 1
2014
+ },
2015
+ thetaStart: {
2016
+ type: "number"
2017
+ },
2018
+ thetaLength: {
2019
+ type: "number"
2020
+ }
2021
+ }
2022
+ },
2023
+ {
2024
+ required: [
2025
+ "points"
2026
+ ],
2027
+ properties: {
2028
+ kind: {
2029
+ const: "lathe"
2030
+ },
2031
+ points: {
2032
+ type: "array",
2033
+ items: {
2034
+ $ref: "#/$defs/vec2"
2035
+ },
2036
+ minItems: 2
2037
+ },
2038
+ segments: {
2039
+ type: "integer",
2040
+ minimum: 1
2041
+ },
2042
+ phiStart: {
2043
+ type: "number"
2044
+ },
2045
+ phiLength: {
2046
+ type: "number"
2047
+ }
2048
+ }
2049
+ },
2050
+ {
2051
+ properties: {
2052
+ kind: {
2053
+ const: "torus"
2054
+ },
2055
+ radius: {
2056
+ type: "number"
2057
+ },
2058
+ tube: {
2059
+ type: "number"
2060
+ },
2061
+ radialSegments: {
2062
+ type: "integer",
2063
+ minimum: 1
2064
+ },
2065
+ tubularSegments: {
2066
+ type: "integer",
2067
+ minimum: 1
2068
+ },
2069
+ arc: {
2070
+ type: "number"
2071
+ }
2072
+ }
2073
+ },
2074
+ {
2075
+ required: [
2076
+ "shape"
2077
+ ],
2078
+ properties: {
2079
+ kind: {
2080
+ const: "extrude"
2081
+ },
2082
+ shape: {
2083
+ type: "object",
2084
+ required: [
2085
+ "outer"
2086
+ ],
2087
+ additionalProperties: false,
2088
+ properties: {
2089
+ outer: {
2090
+ type: "array",
2091
+ items: {
2092
+ $ref: "#/$defs/vec2"
2093
+ },
2094
+ minItems: 3
2095
+ },
2096
+ holes: {
2097
+ type: "array",
2098
+ items: {
2099
+ type: "array",
2100
+ items: {
2101
+ $ref: "#/$defs/vec2"
2102
+ },
2103
+ minItems: 3
2104
+ }
2105
+ }
2106
+ }
2107
+ },
2108
+ depth: {
2109
+ type: "number"
2110
+ },
2111
+ steps: {
2112
+ type: "integer",
2113
+ minimum: 1
2114
+ },
2115
+ bevel: {
2116
+ oneOf: [
2117
+ {
2118
+ const: false
2119
+ },
2120
+ {
2121
+ type: "object",
2122
+ additionalProperties: false,
2123
+ properties: {
2124
+ thickness: {
2125
+ type: "number"
2126
+ },
2127
+ size: {
2128
+ type: "number"
2129
+ },
2130
+ offset: {
2131
+ type: "number"
2132
+ },
2133
+ segments: {
2134
+ type: "integer",
2135
+ minimum: 1
2136
+ }
2137
+ }
2138
+ }
2139
+ ]
2140
+ }
2141
+ }
2142
+ },
2143
+ {
2144
+ required: [
2145
+ "size",
2146
+ "subtract"
2147
+ ],
2148
+ properties: {
2149
+ kind: {
2150
+ const: "difference"
2151
+ },
2152
+ size: {
2153
+ $ref: "#/$defs/vec3"
2154
+ },
2155
+ subtract: {
2156
+ type: "array",
2157
+ items: {
2158
+ type: "object",
2159
+ required: [
2160
+ "min",
2161
+ "max"
2162
+ ],
2163
+ additionalProperties: false,
2164
+ properties: {
2165
+ min: {
2166
+ $ref: "#/$defs/vec2"
2167
+ },
2168
+ max: {
2169
+ $ref: "#/$defs/vec2"
2170
+ }
2171
+ }
2172
+ }
2173
+ }
2174
+ }
2175
+ },
2176
+ {
2177
+ required: [
2178
+ "positions"
2179
+ ],
2180
+ properties: {
2181
+ kind: {
2182
+ const: "mesh"
2183
+ },
2184
+ positions: {
2185
+ type: "array",
2186
+ items: {
2187
+ type: "number"
2188
+ }
2189
+ },
2190
+ normals: {
2191
+ type: "array",
2192
+ items: {
2193
+ type: "number"
2194
+ }
2195
+ },
2196
+ uvs: {
2197
+ type: "array",
2198
+ items: {
2199
+ type: "number"
2200
+ }
2201
+ },
2202
+ indices: {
2203
+ type: "array",
2204
+ items: {
2205
+ $ref: "#/$defs/count"
2206
+ }
2207
+ }
2208
+ }
2209
+ },
2210
+ {
2211
+ properties: {
2212
+ kind: {
2213
+ const: "point"
2214
+ },
2215
+ data: {}
2216
+ }
2217
+ },
2218
+ {
2219
+ required: [
2220
+ "generator"
2221
+ ],
2222
+ properties: {
2223
+ kind: {
2224
+ const: "custom"
2225
+ },
2226
+ generator: {
2227
+ type: "string"
2228
+ },
2229
+ params: {}
2230
+ }
2231
+ }
2232
+ ]
2233
+ }
2234
+ }
2235
+ };
2236
+
2237
+ // src/schema.ts
2238
+ var SHAPE_SCHEMA = shape_schema_default;
2239
+ var num = (v) => typeof v === "number" && Number.isFinite(v) ? null : "expected a finite number";
2240
+ var bool = (v) => typeof v === "boolean" ? null : "expected a boolean";
2241
+ var str = (v) => typeof v === "string" ? null : "expected a string";
2242
+ var int = (min) => (v) => typeof v === "number" && Number.isInteger(v) && v >= min ? null : `expected an integer >= ${min}`;
2243
+ var range = (lo, hi, loOpen = false) => (v) => typeof v === "number" && (loOpen ? v > lo : v >= lo) && v <= hi ? null : `expected a number in ${loOpen ? "(" : "["}${lo}, ${hi}]`;
2244
+ var tuple = (n, item = num) => (v) => {
2245
+ if (!Array.isArray(v) || v.length !== n) return `expected an array of ${n}`;
2246
+ for (const x of v) if (item(x)) return `expected an array of ${n} numbers`;
2247
+ return null;
2248
+ };
2249
+ var list = (item, min = 0) => (v) => {
2250
+ if (!Array.isArray(v)) return "expected an array";
2251
+ if (v.length < min) return `expected at least ${min} items`;
2252
+ for (let i = 0; i < v.length; i++) {
2253
+ const e = item(v[i]);
2254
+ if (e) return `[${i}]: ${e}`;
2255
+ }
2256
+ return null;
2257
+ };
2258
+ var color = (v) => typeof v === "number" && Number.isInteger(v) && v >= 0 && v <= 16777215 || typeof v === "string" && /^#[0-9a-fA-F]{6}$/.test(v) ? null : 'expected 0xRRGGBB or "#rrggbb"';
2259
+ var oneOf = (values) => (v) => values.includes(v) ? null : `expected one of ${values.map((x) => JSON.stringify(x)).join(", ")}`;
2260
+ var object = (fields, required = []) => (v) => {
2261
+ if (!v || typeof v !== "object" || Array.isArray(v)) return "expected an object";
2262
+ const record = v;
2263
+ for (const key of required) if (record[key] === void 0) return `${key}: required`;
2264
+ for (const key of Object.keys(record)) {
2265
+ if (record[key] === void 0) continue;
2266
+ const check = fields[key];
2267
+ if (!check) return `${key}: unknown field`;
2268
+ const e = check(record[key]);
2269
+ if (e) return `${key}: ${e}`;
2270
+ }
2271
+ return null;
2272
+ };
2273
+ var ORDERS = ["XYZ", "YXZ", "ZXY", "ZYX", "YZX", "XZY"];
2274
+ var TRANSFORM = {
2275
+ position: tuple(3),
2276
+ rotation: tuple(3),
2277
+ rotationOrder: oneOf(ORDERS),
2278
+ scale: (v) => typeof v === "number" ? num(v) : tuple(3)(v),
2279
+ heading: num,
2280
+ matrix: tuple(16)
2281
+ };
2282
+ var UV = object({ u0: num, v0: num, u1: num, v1: num }, ["u0", "v0", "u1", "v1"]);
2283
+ var INSTANCE = object({ ...TRANSFORM, color });
2284
+ var COMMON = {
2285
+ ...TRANSFORM,
2286
+ kind: str,
2287
+ name: str,
2288
+ part: str,
2289
+ material: str,
2290
+ color,
2291
+ uv: UV,
2292
+ instances: list(INSTANCE),
2293
+ minDetail: range(0, 1, true),
2294
+ tags: list(str)
2295
+ };
2296
+ var vec2 = tuple(2);
2297
+ var BEVEL = (v) => v === false ? null : object({ thickness: num, size: num, offset: num, segments: int(1) })(v);
2298
+ var KINDS = {
2299
+ group: { fields: {} },
2300
+ box: { fields: { size: tuple(3), segments: tuple(3, int(1)) } },
2301
+ cylinder: { fields: { radiusTop: num, radiusBottom: num, height: num, radialSegments: int(1), heightSegments: int(1), openEnded: bool, thetaStart: num, thetaLength: num } },
2302
+ cone: { fields: { radius: num, height: num, radialSegments: int(1), heightSegments: int(1), openEnded: bool, thetaStart: num, thetaLength: num } },
2303
+ sphere: { fields: { radius: num, widthSegments: int(3), heightSegments: int(2), phiStart: num, phiLength: num, thetaStart: num, thetaLength: num } },
2304
+ plane: { fields: { width: num, height: num, widthSegments: int(1), heightSegments: int(1) } },
2305
+ circle: { fields: { radius: num, segments: int(3), thetaStart: num, thetaLength: num } },
2306
+ ring: { fields: { innerRadius: num, outerRadius: num, thetaSegments: int(3), phiSegments: int(1), thetaStart: num, thetaLength: num } },
2307
+ lathe: { fields: { points: list(vec2, 2), segments: int(1), phiStart: num, phiLength: num }, required: ["points"] },
2308
+ torus: { fields: { radius: num, tube: num, radialSegments: int(1), tubularSegments: int(1), arc: num } },
2309
+ extrude: {
2310
+ fields: { shape: object({ outer: list(vec2, 3), holes: list(list(vec2, 3)) }, ["outer"]), depth: num, steps: int(1), bevel: BEVEL },
2311
+ required: ["shape"]
2312
+ },
2313
+ difference: { fields: { size: tuple(3), subtract: list(object({ min: vec2, max: vec2 }, ["min", "max"])) }, required: ["size", "subtract"] },
2314
+ mesh: {
2315
+ fields: {
2316
+ positions: (v) => list(num)(v) ?? (v.length % 3 === 0 ? null : "expected a multiple of 3 numbers"),
2317
+ normals: list(num),
2318
+ uvs: list(num),
2319
+ indices: list(int(0))
2320
+ },
2321
+ required: ["positions"]
2322
+ },
2323
+ point: { fields: { data: () => null } },
2324
+ custom: { fields: { generator: str, params: () => null }, required: ["generator"] }
2325
+ };
2326
+ function validateNode(node, path, errors) {
2327
+ if (!node || typeof node !== "object" || Array.isArray(node)) {
2328
+ errors.push(`${path}: expected a node object`);
2329
+ return;
2330
+ }
2331
+ const record = node;
2332
+ const spec = typeof record.kind === "string" ? KINDS[record.kind] : void 0;
2333
+ if (!spec) {
2334
+ errors.push(`${path}.kind: expected one of ${Object.keys(KINDS).join(", ")}`);
2335
+ return;
2336
+ }
2337
+ for (const key of spec.required ?? []) if (record[key] === void 0) errors.push(`${path}.${key}: required`);
2338
+ for (const key of Object.keys(record)) {
2339
+ if (record[key] === void 0 || key === "children") continue;
2340
+ const check = spec.fields[key] ?? COMMON[key];
2341
+ if (!check) {
2342
+ errors.push(`${path}.${key}: unknown field for a ${String(record.kind)}`);
2343
+ continue;
2344
+ }
2345
+ const e = check(record[key]);
2346
+ if (e) errors.push(`${path}.${key}: ${e}`);
2347
+ }
2348
+ const exclusive = ["matrix", "heading"].filter((key) => record[key] !== void 0);
2349
+ if (record.matrix !== void 0 && (record.position !== void 0 || record.rotation !== void 0 || record.scale !== void 0 || record.heading !== void 0))
2350
+ errors.push(`${path}.matrix: cannot be combined with position, rotation, scale or heading`);
2351
+ else if (exclusive.includes("heading") && (record.rotation !== void 0 || record.scale !== void 0)) errors.push(`${path}.heading: cannot be combined with rotation or scale`);
2352
+ if (record.children !== void 0) {
2353
+ if (record.kind !== "group") errors.push(`${path}.children: only a group has children`);
2354
+ else if (!Array.isArray(record.children)) errors.push(`${path}.children: expected an array`);
2355
+ else record.children.forEach((child, index) => validateNode(child, `${path}.children[${index}]`, errors));
2356
+ }
2357
+ }
2358
+ function validateShape(schema) {
2359
+ const errors = [];
2360
+ if (!schema || typeof schema !== "object" || Array.isArray(schema)) return ["expected a shape object"];
2361
+ const record = schema;
2362
+ for (const key of Object.keys(record)) if (!["version", "name", "materials", "root", "lod", "cullDistance"].includes(key) && record[key] !== void 0) errors.push(`${key}: unknown field`);
2363
+ if (record.version !== 1) errors.push("version: expected 1");
2364
+ if (record.name !== void 0 && typeof record.name !== "string") errors.push("name: expected a string");
2365
+ if (record.materials !== void 0) {
2366
+ if (!record.materials || typeof record.materials !== "object" || Array.isArray(record.materials)) errors.push("materials: expected an object");
2367
+ else {
2368
+ const material = object({
2369
+ color,
2370
+ roughness: range(0, 1),
2371
+ metalness: range(0, 1),
2372
+ opacity: range(0, 1),
2373
+ transparent: bool,
2374
+ emissive: color,
2375
+ emissiveIntensity: (v) => typeof v === "number" && v >= 0 ? null : "expected a number >= 0",
2376
+ side: oneOf(["front", "back", "double"]),
2377
+ flatShading: bool,
2378
+ vertexColors: bool,
2379
+ depthWrite: bool
2380
+ });
2381
+ for (const [id, hint] of Object.entries(record.materials)) {
2382
+ const e = material(hint);
2383
+ if (e) errors.push(`materials.${id}: ${e}`);
2384
+ }
2385
+ }
2386
+ }
2387
+ if (record.lod !== void 0) {
2388
+ const e = list(object({ distance: (v) => typeof v === "number" && v >= 0 ? null : "expected a number >= 0", detail: range(0, 1, true), minSize: (v) => typeof v === "number" && v >= 0 ? null : "expected a number >= 0", minSegments: int(3) }, ["distance", "detail"]))(record.lod);
2389
+ if (e) errors.push(`lod${e.startsWith("[") ? "" : ": "}${e}`);
2390
+ }
2391
+ if (record.cullDistance !== void 0 && !(typeof record.cullDistance === "number" && record.cullDistance > 0)) errors.push("cullDistance: expected a number > 0");
2392
+ if (record.root === void 0) errors.push("root: required");
2393
+ else validateNode(record.root, "root", errors);
2394
+ return errors;
2395
+ }
2396
+ function assertValidShape(schema) {
2397
+ const errors = validateShape(schema);
2398
+ if (errors.length) throw new Error(`game-shapes: invalid shape
2399
+ ${errors.slice(0, 20).join("\n ")}${errors.length > 20 ? `
2400
+ ... ${errors.length - 20} more` : ""}`);
2401
+ }
2402
+
2403
+ // src/generate.ts
2404
+ var PRIMITIVE_KINDS = /* @__PURE__ */ new Set(["box", "cylinder", "cone", "sphere", "plane", "circle", "ring", "lathe", "torus", "extrude", "difference", "mesh"]);
2405
+ var isPrimitiveNode = (node) => PRIMITIVE_KINDS.has(node.kind);
2406
+ function keyOptions(options = {}) {
2407
+ return {
2408
+ seed: options.seed ?? 0,
2409
+ colors: options.colors ?? "linear",
2410
+ indexed: options.indexed ?? true,
2411
+ materials: options.materials,
2412
+ fallbackMaterial: options.fallbackMaterial,
2413
+ materialAlias: options.materialAlias,
2414
+ instancing: options.instancing,
2415
+ levels: options.levels
2416
+ };
2417
+ }
2418
+ var shapeKey = (schema, options = {}) => hashValue({ schema, options: keyOptions(options) });
2419
+ var IDENTITY_UV = { u0: 0, v0: 0, u1: 1, v1: 1 };
2420
+ var FloatList = class {
2421
+ constructor(capacity = 256) {
2422
+ __publicField(this, "array");
2423
+ __publicField(this, "length", 0);
2424
+ this.array = new Float32Array(capacity);
2425
+ }
2426
+ reserve(extra) {
2427
+ if (this.length + extra <= this.array.length) return;
2428
+ let capacity = this.array.length * 2;
2429
+ while (capacity < this.length + extra) capacity *= 2;
2430
+ const next = new Float32Array(capacity);
2431
+ next.set(this.array.subarray(0, this.length));
2432
+ this.array = next;
2433
+ }
2434
+ done() {
2435
+ return this.array.slice(0, this.length);
2436
+ }
2437
+ };
2438
+ var IndexList = class {
2439
+ constructor(capacity = 256) {
2440
+ __publicField(this, "array");
2441
+ __publicField(this, "length", 0);
2442
+ this.array = new Uint32Array(capacity);
2443
+ }
2444
+ reserve(extra) {
2445
+ if (this.length + extra <= this.array.length) return;
2446
+ let capacity = this.array.length * 2;
2447
+ while (capacity < this.length + extra) capacity *= 2;
2448
+ const next = new Uint32Array(capacity);
2449
+ next.set(this.array.subarray(0, this.length));
2450
+ this.array = next;
2451
+ }
2452
+ };
2453
+ var Accumulator = class {
2454
+ constructor(material, part) {
2455
+ __publicField(this, "material", material);
2456
+ __publicField(this, "part", part);
2457
+ __publicField(this, "positions", new FloatList());
2458
+ __publicField(this, "normals", new FloatList());
2459
+ __publicField(this, "uvs", new FloatList(170));
2460
+ __publicField(this, "colors", new FloatList());
2461
+ __publicField(this, "indices", new IndexList());
2462
+ __publicField(this, "ranges", []);
2463
+ __publicField(this, "vertices", 0);
2464
+ }
2465
+ };
2466
+ function bake(acc, t, world, rgb, u, indexed) {
2467
+ const e = world;
2468
+ const nm = normalMatrix(world);
2469
+ const affine = isAffine(world);
2470
+ const source = t.positions;
2471
+ const count = source.length / 3;
2472
+ const order = indexed ? null : t.indices ?? null;
2473
+ const emitted = order ? order.length : count;
2474
+ acc.positions.reserve(emitted * 3);
2475
+ acc.normals.reserve(emitted * 3);
2476
+ acc.uvs.reserve(emitted * 2);
2477
+ if (rgb) acc.colors.reserve(emitted * 3);
2478
+ const P = acc.positions.array;
2479
+ const N = acc.normals.array;
2480
+ const U = acc.uvs.array;
2481
+ const C = acc.colors.array;
2482
+ let pi = acc.positions.length;
2483
+ let ni = acc.normals.length;
2484
+ let ui = acc.uvs.length;
2485
+ let ci = acc.colors.length;
2486
+ const du = u.u1 - u.u0;
2487
+ const dv = u.v1 - u.v0;
2488
+ const tn = t.normals;
2489
+ const tu = t.uvs;
2490
+ for (let k = 0; k < emitted; k++) {
2491
+ const i = order ? order[k] : k;
2492
+ const x = source[i * 3];
2493
+ const y = source[i * 3 + 1];
2494
+ const z = source[i * 3 + 2];
2495
+ if (affine) {
2496
+ P[pi++] = e[0] * x + e[4] * y + e[8] * z + e[12];
2497
+ P[pi++] = e[1] * x + e[5] * y + e[9] * z + e[13];
2498
+ P[pi++] = e[2] * x + e[6] * y + e[10] * z + e[14];
2499
+ } else {
2500
+ const p = transformPoint(e, x, y, z);
2501
+ P[pi++] = p[0];
2502
+ P[pi++] = p[1];
2503
+ P[pi++] = p[2];
2504
+ }
2505
+ const nx = tn[i * 3];
2506
+ const ny = tn[i * 3 + 1];
2507
+ const nz = tn[i * 3 + 2];
2508
+ const vx = nm[0] * nx + nm[3] * ny + nm[6] * nz;
2509
+ const vy = nm[1] * nx + nm[4] * ny + nm[7] * nz;
2510
+ const vz = nm[2] * nx + nm[5] * ny + nm[8] * nz;
2511
+ const s = 1 / (Math.sqrt(vx * vx + vy * vy + vz * vz) || 1);
2512
+ N[ni++] = vx * s;
2513
+ N[ni++] = vy * s;
2514
+ N[ni++] = vz * s;
2515
+ U[ui++] = u.u0 + tu[i * 2] * du;
2516
+ U[ui++] = u.v0 + tu[i * 2 + 1] * dv;
2517
+ if (rgb) {
2518
+ C[ci++] = rgb[0];
2519
+ C[ci++] = rgb[1];
2520
+ C[ci++] = rgb[2];
2521
+ }
2522
+ }
2523
+ acc.positions.length = pi;
2524
+ acc.normals.length = ni;
2525
+ acc.uvs.length = ui;
2526
+ acc.colors.length = ci;
2527
+ if (indexed) {
2528
+ const base = acc.vertices;
2529
+ const idx = t.indices;
2530
+ const n = idx ? idx.length : count;
2531
+ acc.indices.reserve(n);
2532
+ const I = acc.indices.array;
2533
+ let ii = acc.indices.length;
2534
+ for (let k = 0; k < n; k++) I[ii++] = base + (idx ? idx[k] : k);
2535
+ acc.indices.length = ii;
2536
+ }
2537
+ acc.vertices += emitted;
2538
+ }
2539
+ function boundsOf(positions) {
2540
+ const min = [Infinity, Infinity, Infinity];
2541
+ const max = [-Infinity, -Infinity, -Infinity];
2542
+ for (let i = 0; i < positions.length; i += 3) {
2543
+ for (let axis = 0; axis < 3; axis++) {
2544
+ const v = positions[i + axis];
2545
+ if (v < min[axis]) min[axis] = v;
2546
+ if (v > max[axis]) max[axis] = v;
2547
+ }
2548
+ }
2549
+ return { min, max };
2550
+ }
2551
+ function finish(acc, indexed, withColors) {
2552
+ const positions = acc.positions.done();
2553
+ const indices = indexed ? acc.vertices > 65535 ? acc.indices.array.slice(0, acc.indices.length) : Uint16Array.from(acc.indices.array.subarray(0, acc.indices.length)) : void 0;
2554
+ return {
2555
+ material: acc.material,
2556
+ part: acc.part,
2557
+ positions,
2558
+ normals: acc.normals.done(),
2559
+ uvs: acc.uvs.done(),
2560
+ ...withColors ? { colors: acc.colors.done() } : {},
2561
+ ...indices ? { indices } : {},
2562
+ ranges: acc.ranges,
2563
+ bounds: boundsOf(positions)
2564
+ };
2565
+ }
2566
+ var radiusOf = (t, world) => {
2567
+ let r = 0;
2568
+ const p = t.positions;
2569
+ for (let i = 0; i < p.length; i += 3) r = Math.max(r, Math.hypot(p[i], p[i + 1], p[i + 2]));
2570
+ const sx = Math.hypot(world[0], world[1], world[2]);
2571
+ const sy = Math.hypot(world[4], world[5], world[6]);
2572
+ const sz = Math.hypot(world[8], world[9], world[10]);
2573
+ return r * Math.max(sx, sy, sz);
2574
+ };
2575
+ function expandCustom(node, settings, detail, path) {
2576
+ const generator = settings.generators?.[node.generator] ?? getGenerator(node.generator);
2577
+ if (!generator) throw new Error(`game-shapes: no generator "${node.generator}" (register it with registerGenerator, in the worker too) at ${path}`);
2578
+ const generated = generator(node.params, { random: streamFor(settings.seed, node.generator, path), seed: settings.seed, detail, path });
2579
+ const { kind: _kind, generator: _generator, params: _params, ...common } = node;
2580
+ return { ...common, kind: "group", children: [generated] };
2581
+ }
2582
+ var leafKey = (node, material, color2, uv, level) => `${node.kind}|${canonicalJson(primitiveParameters(node, level))}|${material}|${color2}|${uv.u0},${uv.v0},${uv.u1},${uv.v1}`;
2583
+ function runLevel(schema, settings, level) {
2584
+ const bakeParts = level.index > 0;
2585
+ const root = { part: null, accs: /* @__PURE__ */ new Map() };
2586
+ const targets = [root];
2587
+ const partTargets = /* @__PURE__ */ new Map();
2588
+ const parts = [];
2589
+ const points = [];
2590
+ const instanced = [];
2591
+ let primitives = 0;
2592
+ const space = settings.colors === "none" ? null : settings.colors;
2593
+ const channels = /* @__PURE__ */ new Map();
2594
+ const rgbOf = (hex) => {
2595
+ if (!space) return null;
2596
+ let c = channels.get(hex);
2597
+ if (!c) {
2598
+ c = colorChannels(hex, space);
2599
+ channels.set(hex, c);
2600
+ }
2601
+ return c;
2602
+ };
2603
+ const resolveMaterial = (id) => {
2604
+ let out = id;
2605
+ if (settings.materials && !settings.materials.has(out)) out = settings.fallback ?? out;
2606
+ return settings.alias?.[out] ?? out;
2607
+ };
2608
+ const accOf = (target, material) => {
2609
+ let acc = target.accs.get(material);
2610
+ if (!acc) {
2611
+ acc = new Accumulator(material, target.part);
2612
+ target.accs.set(material, acc);
2613
+ }
2614
+ return acc;
2615
+ };
2616
+ const autoCounts = /* @__PURE__ */ new Map();
2617
+ const autoGroups = /* @__PURE__ */ new Map();
2618
+ const count = (node, inherited, path) => {
2619
+ if (node.minDetail !== void 0 && level.detail < node.minDetail) return;
2620
+ if (node.kind === "custom") return count(expandCustom(node, settings, level.detail, path), inherited, path);
2621
+ const next = {
2622
+ material: node.material !== void 0 ? resolveMaterial(node.material) : inherited.material,
2623
+ color: node.color !== void 0 ? colorHex(node.color) : inherited.color,
2624
+ uv: node.uv ?? inherited.uv,
2625
+ inPart: inherited.inPart || node.part !== void 0 && !bakeParts
2626
+ };
2627
+ if (node.instances) return;
2628
+ if (node.kind === "group") {
2629
+ node.children?.forEach((child, index) => count(child, next, `${path}/${index}`));
2630
+ return;
2631
+ }
2632
+ if (isPrimitiveNode(node) && node.name === void 0 && !next.inPart) {
2633
+ const key = leafKey(node, next.material, next.color, next.uv, level);
2634
+ autoCounts.set(key, (autoCounts.get(key) ?? 0) + 1);
2635
+ }
2636
+ };
2637
+ const rootMaterial = resolveMaterial("default");
2638
+ if (settings.autoMin !== void 0) count(schema.root, { material: rootMaterial, color: 16777215, uv: IDENTITY_UV, inPart: false }, "root");
2639
+ const visit = (node, parent, inherited, path) => {
2640
+ if (node.minDetail !== void 0 && level.detail < node.minDetail) return;
2641
+ if (node.kind === "custom") return visit(expandCustom(node, settings, level.detail, path), parent, inherited, path);
2642
+ let target = inherited.target;
2643
+ let parentWorld = parent;
2644
+ if (node.part !== void 0) {
2645
+ if (bakeParts) {
2646
+ parentWorld = null;
2647
+ } else {
2648
+ let partTarget = partTargets.get(node.part);
2649
+ if (!partTarget) {
2650
+ partTarget = { part: node.part, accs: /* @__PURE__ */ new Map() };
2651
+ partTargets.set(node.part, partTarget);
2652
+ targets.push(partTarget);
2653
+ const { position, rotation, rotationOrder, scale, heading, matrix } = node;
2654
+ parts.push({
2655
+ id: node.part,
2656
+ ...position ? { position } : {},
2657
+ ...rotation ? { rotation } : {},
2658
+ ...rotationOrder ? { rotationOrder } : {},
2659
+ ...scale !== void 0 ? { scale } : {},
2660
+ ...heading !== void 0 ? { heading } : {},
2661
+ ...matrix ? { matrix } : { matrix: asMat4(matrixOf(node)) }
2662
+ });
2663
+ }
2664
+ target = partTarget;
2665
+ }
2666
+ }
2667
+ const isPart = node.part !== void 0 && !bakeParts;
2668
+ const local = isPart ? null : matrixOf(node);
2669
+ const world = isPart ? null : local ? parentWorld ? multiply(parentWorld, local) : local : parentWorld;
2670
+ const next = {
2671
+ material: node.material !== void 0 ? resolveMaterial(node.material) : inherited.material,
2672
+ color: node.color !== void 0 ? colorHex(node.color) : inherited.color,
2673
+ uv: node.uv ?? inherited.uv,
2674
+ target
2675
+ };
2676
+ if (node.instances) {
2677
+ drawInstanced(node, parentWorld, next, path);
2678
+ return;
2679
+ }
2680
+ const snapshot = node.name !== void 0 ? new Map([...target.accs].map(([m, a]) => [m, { v: a.vertices, i: a.indices.length }])) : null;
2681
+ if (node.kind === "group") {
2682
+ node.children?.forEach((child, index) => visit(child, world, next, `${path}/${index}`));
2683
+ } else if (node.kind === "point") {
2684
+ const p = node.position ?? [0, 0, 0];
2685
+ points.push({
2686
+ ...node.name !== void 0 ? { name: node.name } : {},
2687
+ part: target.part,
2688
+ position: parent ? transformPoint(parent, p[0], p[1], p[2]) : [p[0], p[1], p[2]],
2689
+ ...node.data !== void 0 ? { data: node.data } : {},
2690
+ ...node.tags ? { tags: node.tags } : {}
2691
+ });
2692
+ } else if (isPrimitiveNode(node)) {
2693
+ const template = buildPrimitive(node, level);
2694
+ const m = world ?? identity4();
2695
+ if (level.minSize !== void 0 && radiusOf(template, m) < level.minSize) return;
2696
+ primitives++;
2697
+ if (settings.autoMin !== void 0 && node.name === void 0 && target.part === null) {
2698
+ const key = leafKey(node, next.material, next.color, next.uv, level);
2699
+ if ((autoCounts.get(key) ?? 0) >= settings.autoMin) {
2700
+ let group = autoGroups.get(key);
2701
+ if (!group) {
2702
+ group = { node, material: next.material, color: next.color, part: null, matrices: [] };
2703
+ autoGroups.set(key, group);
2704
+ }
2705
+ group.matrices.push(...m);
2706
+ return;
2707
+ }
2708
+ }
2709
+ bake(accOf(target, next.material), template, m, rgbOf(next.color), next.uv, settings.indexed);
2710
+ }
2711
+ if (snapshot) {
2712
+ for (const [material, acc] of target.accs) {
2713
+ const before = snapshot.get(material) ?? { v: 0, i: 0 };
2714
+ if (acc.vertices === before.v) continue;
2715
+ acc.ranges.push({
2716
+ name: node.name,
2717
+ ...node.tags ? { tags: node.tags } : {},
2718
+ vertexStart: before.v,
2719
+ vertexCount: acc.vertices - before.v,
2720
+ indexStart: before.i,
2721
+ indexCount: acc.indices.length - before.i
2722
+ });
2723
+ }
2724
+ }
2725
+ };
2726
+ const drawInstanced = (node, parentWorld, inherited, path) => {
2727
+ const collector = { part: inherited.target.part, accs: /* @__PURE__ */ new Map() };
2728
+ const { instances, ...single } = node;
2729
+ const before = parts.length;
2730
+ visit(single, null, { ...inherited, target: collector }, path);
2731
+ if (parts.length !== before) throw new Error(`game-shapes: an instanced node cannot hold parts (${path})`);
2732
+ const matrices = new Float32Array(instances.length * 16);
2733
+ const hasColors = instances.some((instance) => instance.color !== void 0);
2734
+ const colors = hasColors ? new Float32Array(instances.length * 3) : void 0;
2735
+ instances.forEach((instance, index) => {
2736
+ const local = matrixOf(instance);
2737
+ const m = local ? parentWorld ? multiply(parentWorld, local) : local : parentWorld ?? identity4();
2738
+ matrices.set(m, index * 16);
2739
+ if (colors) colors.set(colorChannels(colorHex(instance.color), space ?? "srgb"), index * 3);
2740
+ });
2741
+ for (const acc of collector.accs.values()) {
2742
+ const batch = finish(acc, settings.indexed, false);
2743
+ instanced.push({
2744
+ material: acc.material,
2745
+ part: collector.part,
2746
+ ...node.name !== void 0 ? { name: node.name } : {},
2747
+ geometry: { type: "BufferGeometry", parameters: {}, positions: batch.positions, normals: batch.normals, uvs: batch.uvs, ...batch.indices ? { indices: batch.indices } : {}, groups: [] },
2748
+ matrices: matrices.slice(),
2749
+ ...colors ? { colors: colors.slice() } : {},
2750
+ count: instances.length
2751
+ });
2752
+ }
2753
+ };
2754
+ visit(schema.root, null, { material: rootMaterial, color: 16777215, uv: IDENTITY_UV, target: root }, "root");
2755
+ for (const group of autoGroups.values()) {
2756
+ const template = buildPrimitive(group.node, level);
2757
+ const n = group.matrices.length / 16;
2758
+ const rgb = colorChannels(group.color, space ?? "srgb");
2759
+ const colors = new Float32Array(n * 3);
2760
+ for (let i = 0; i < n; i++) colors.set(rgb, i * 3);
2761
+ instanced.push({
2762
+ material: group.material,
2763
+ part: null,
2764
+ geometry: { type: template.type, parameters: template.parameters, positions: template.positions.slice(), normals: template.normals.slice(), uvs: template.uvs.slice(), ...template.indices ? { indices: template.indices.slice() } : {}, groups: template.groups.map((g) => ({ ...g })) },
2765
+ matrices: new Float32Array(group.matrices),
2766
+ ...space ? { colors } : {},
2767
+ count: n
2768
+ });
2769
+ }
2770
+ const batches = [];
2771
+ for (const target of targets) for (const acc of target.accs.values()) if (acc.vertices > 0 && (!settings.indexed || acc.indices.length > 0)) batches.push(finish(acc, settings.indexed, space !== null));
2772
+ return { level: { distance: level.distance, detail: level.detail, batches, instanced }, parts, points, primitives };
2773
+ }
2774
+ function generate(schema, options = {}) {
2775
+ if (options.validate !== false) assertValidShape(schema);
2776
+ const settings = {
2777
+ colors: options.colors ?? "linear",
2778
+ indexed: options.indexed ?? true,
2779
+ ...options.materials ? { materials: new Set(options.materials) } : {},
2780
+ ...options.fallbackMaterial !== void 0 ? { fallback: options.fallbackMaterial } : {},
2781
+ ...options.materialAlias ? { alias: options.materialAlias } : {},
2782
+ seed: options.seed ?? 0,
2783
+ ...options.generators ? { generators: options.generators } : {},
2784
+ ...options.instancing ? { autoMin: Math.max(2, options.instancing.minCount) } : {}
2785
+ };
2786
+ const specs = [{ distance: 0, detail: 1, index: 0 }, ...(schema.lod ?? []).map((rule, index) => ({ ...rule, index: index + 1 }))];
2787
+ const wanted = options.levels ? specs.filter((spec) => options.levels.includes(spec.index)) : specs;
2788
+ let parts = [];
2789
+ let points = [];
2790
+ let primitives = 0;
2791
+ const levels = [];
2792
+ for (const spec of wanted) {
2793
+ const result = runLevel(schema, settings, spec);
2794
+ if (spec.index === 0 || levels.length === 0) {
2795
+ parts = result.parts;
2796
+ points = result.points;
2797
+ primitives = result.primitives;
2798
+ }
2799
+ levels.push(result.level);
2800
+ }
2801
+ const first = levels[0];
2802
+ const vertices = first ? first.batches.reduce((sum, batch) => sum + batch.positions.length / 3, 0) : 0;
2803
+ const triangles = first ? first.batches.reduce((sum, batch) => sum + (batch.indices ? batch.indices.length : batch.positions.length / 3) / 3, 0) + first.instanced.reduce((sum, batch) => sum + (batch.geometry.indices ? batch.geometry.indices.length : batch.geometry.positions.length / 3) / 3 * batch.count, 0) : 0;
2804
+ return {
2805
+ key: options.key === false ? "" : options.key ?? shapeKey(schema, options),
2806
+ ...schema.name !== void 0 ? { name: schema.name } : {},
2807
+ levels,
2808
+ parts,
2809
+ points,
2810
+ ...schema.materials ? { materials: schema.materials } : {},
2811
+ ...schema.cullDistance !== void 0 ? { cullDistance: schema.cullDistance } : {},
2812
+ stats: { vertices, triangles, batches: first ? first.batches.length : 0, instances: first ? first.instanced.reduce((sum, batch) => sum + batch.count, 0) : 0, primitives }
2813
+ };
2814
+ }
2815
+
2816
+ // src/merge.ts
2817
+ function mergeTransformed(pieces) {
2818
+ let total = 0;
2819
+ for (const piece of pieces) total += piece.indices ? piece.indices.length : piece.positions.length / 3;
2820
+ const positions = new Float32Array(total * 3);
2821
+ const normals = new Float32Array(total * 3);
2822
+ const uvs = new Float32Array(total * 2);
2823
+ let at = 0;
2824
+ for (const piece of pieces) {
2825
+ const e = piece.matrix;
2826
+ const nm = normalMatrix(e);
2827
+ const count = piece.indices ? piece.indices.length : piece.positions.length / 3;
2828
+ const p = new Float32Array(count * 3);
2829
+ const n = new Float32Array(count * 3);
2830
+ for (let k = 0; k < count; k++) {
2831
+ const i = piece.indices ? piece.indices[k] : k;
2832
+ for (let c = 0; c < 3; c++) {
2833
+ p[k * 3 + c] = piece.positions[i * 3 + c];
2834
+ if (piece.normals) n[k * 3 + c] = piece.normals[i * 3 + c];
2835
+ }
2836
+ if (piece.uvs) {
2837
+ uvs[(at + k) * 2] = piece.uvs[i * 2];
2838
+ uvs[(at + k) * 2 + 1] = piece.uvs[i * 2 + 1];
2839
+ }
2840
+ }
2841
+ for (let k = 0; k < count; k++) {
2842
+ const q = transformPoint(e, p[k * 3], p[k * 3 + 1], p[k * 3 + 2]);
2843
+ positions[(at + k) * 3] = q[0];
2844
+ positions[(at + k) * 3 + 1] = q[1];
2845
+ positions[(at + k) * 3 + 2] = q[2];
2846
+ const x = n[k * 3];
2847
+ const y = n[k * 3 + 1];
2848
+ const z = n[k * 3 + 2];
2849
+ const vx = nm[0] * x + nm[3] * y + nm[6] * z;
2850
+ const vy = nm[1] * x + nm[4] * y + nm[7] * z;
2851
+ const vz = nm[2] * x + nm[5] * y + nm[8] * z;
2852
+ const s = 1 / (Math.sqrt(vx * vx + vy * vy + vz * vz) || 1);
2853
+ normals[(at + k) * 3] = vx * s;
2854
+ normals[(at + k) * 3 + 1] = vy * s;
2855
+ normals[(at + k) * 3 + 2] = vz * s;
2856
+ }
2857
+ at += count;
2858
+ }
2859
+ return { positions, normals, uvs };
2860
+ }
2861
+ var primitiveArrays = (p) => [p.positions, p.normals, p.uvs, ...p.indices ? [p.indices] : []].map((a) => a.buffer);
2862
+ var batchArrays = (b) => [b.positions, b.normals, b.uvs, ...b.colors ? [b.colors] : [], ...b.indices ? [b.indices] : []].map((a) => a.buffer);
2863
+ var instancedArrays = (b) => [...primitiveArrays(b.geometry), b.matrices.buffer, ...b.colors ? [b.colors.buffer] : []];
2864
+ function transferablesOf(buffers) {
2865
+ const out = /* @__PURE__ */ new Set();
2866
+ for (const level of buffers.levels) {
2867
+ for (const batch of level.batches) for (const a of batchArrays(batch)) out.add(a);
2868
+ for (const batch of level.instanced) for (const a of instancedArrays(batch)) out.add(a);
2869
+ }
2870
+ return [...out];
2871
+ }
2872
+ var copyPrimitive = (p) => ({
2873
+ ...p,
2874
+ positions: p.positions.slice(),
2875
+ normals: p.normals.slice(),
2876
+ uvs: p.uvs.slice(),
2877
+ ...p.indices ? { indices: p.indices.slice() } : {},
2878
+ groups: p.groups.map((g) => ({ ...g }))
2879
+ });
2880
+ function cloneBuffers(buffers) {
2881
+ return {
2882
+ ...buffers,
2883
+ levels: buffers.levels.map((level) => ({
2884
+ ...level,
2885
+ batches: level.batches.map((b) => ({
2886
+ ...b,
2887
+ positions: b.positions.slice(),
2888
+ normals: b.normals.slice(),
2889
+ uvs: b.uvs.slice(),
2890
+ ...b.colors ? { colors: b.colors.slice() } : {},
2891
+ ...b.indices ? { indices: b.indices.slice() } : {},
2892
+ ranges: b.ranges.map((r) => ({ ...r })),
2893
+ bounds: { min: [...b.bounds.min], max: [...b.bounds.max] }
2894
+ })),
2895
+ instanced: level.instanced.map((b) => ({ ...b, geometry: copyPrimitive(b.geometry), matrices: b.matrices.slice(), ...b.colors ? { colors: b.colors.slice() } : {} }))
2896
+ }))
2897
+ };
2898
+ }
2899
+ function byteSizeOf(buffers) {
2900
+ return transferablesOf(buffers).reduce((sum, buffer) => sum + buffer.byteLength, 0);
2901
+ }
2902
+
2903
+ // src/worker.ts
2904
+ function installShapeWorker(scope, options = {}) {
2905
+ const transfer = options.transfer !== false;
2906
+ scope.onmessage = (event) => {
2907
+ const job = event.data;
2908
+ try {
2909
+ const buffers = generate(job.schema, { ...job.options ?? {}, ...options.generators ? { generators: options.generators } : {} });
2910
+ const reply = { id: job.id, ok: true, buffers };
2911
+ scope.postMessage(reply, transfer ? transferablesOf(buffers) : []);
2912
+ } catch (error) {
2913
+ const reply = { id: job.id, ok: false, error: error instanceof Error ? error.message : String(error) };
2914
+ scope.postMessage(reply);
2915
+ }
2916
+ };
2917
+ }
2918
+
2919
+ export {
2920
+ colorHex,
2921
+ srgbToLinear,
2922
+ colorChannels,
2923
+ canonicalJson,
2924
+ hash64,
2925
+ hashValue,
2926
+ identity4,
2927
+ quaternionFromEuler,
2928
+ compose,
2929
+ headingMatrix,
2930
+ multiply,
2931
+ normalMatrix,
2932
+ isAffine,
2933
+ transformPoint,
2934
+ matrixOf,
2935
+ box,
2936
+ cylinder,
2937
+ cone,
2938
+ sphere,
2939
+ plane,
2940
+ circle,
2941
+ ring,
2942
+ lathe,
2943
+ torus,
2944
+ difference,
2945
+ computeVertexNormals,
2946
+ extrude,
2947
+ primitiveParameters,
2948
+ setTemplateCacheSize,
2949
+ clearTemplateCache,
2950
+ templateCacheStats,
2951
+ buildPrimitive,
2952
+ clonePrimitive,
2953
+ mulberry32,
2954
+ streamFor,
2955
+ registerGenerator,
2956
+ unregisterGenerator,
2957
+ getGenerator,
2958
+ listGenerators,
2959
+ SHAPE_SCHEMA,
2960
+ validateShape,
2961
+ assertValidShape,
2962
+ isPrimitiveNode,
2963
+ keyOptions,
2964
+ shapeKey,
2965
+ generate,
2966
+ mergeTransformed,
2967
+ transferablesOf,
2968
+ cloneBuffers,
2969
+ byteSizeOf,
2970
+ installShapeWorker
2971
+ };
2972
+ //# sourceMappingURL=chunk-R4RY23RH.js.map