@adatechnology/game-urban-three 0.1.0 → 0.2.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.
@@ -36,10 +36,1528 @@ var GameUrbanThree = (() => {
36
36
  loadUrbanTestModule: () => loadUrbanTestModule
37
37
  });
38
38
 
39
+ // ../game-shapes/dist/chunk-R4RY23RH.js
40
+ function canonicalJson(value) {
41
+ if (value === null) return "null";
42
+ if (typeof value === "number") return Object.is(value, -0) ? "-0" : Number.isFinite(value) ? String(value) : JSON.stringify(String(value));
43
+ if (typeof value === "string") return JSON.stringify(value);
44
+ if (typeof value === "boolean") return value ? "true" : "false";
45
+ if (typeof value === "function") throw new Error("game-shapes: a function cannot be part of a shape (register it as a generator)");
46
+ if (ArrayBuffer.isView(value)) return canonicalJson(Array.from(value));
47
+ if (Array.isArray(value)) return `[${value.map((item) => item === void 0 ? "null" : canonicalJson(item)).join(",")}]`;
48
+ if (typeof value === "object") {
49
+ const record = value;
50
+ const keys = Object.keys(record).filter((key) => record[key] !== void 0).sort();
51
+ return `{${keys.map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(",")}}`;
52
+ }
53
+ return "null";
54
+ }
55
+ function needsUint32(array) {
56
+ for (let i = array.length - 1; i >= 0; --i) if (array[i] >= 65535) return true;
57
+ return false;
58
+ }
59
+ var indexArray = (indices) => needsUint32(indices) ? new Uint32Array(indices) : new Uint16Array(indices);
60
+ function pack(type, parameters, a) {
61
+ return {
62
+ type,
63
+ parameters,
64
+ positions: new Float32Array(a.vertices),
65
+ normals: new Float32Array(a.normals),
66
+ uvs: new Float32Array(a.uvs),
67
+ ...a.indices ? { indices: indexArray(a.indices) } : {},
68
+ groups: a.groups
69
+ };
70
+ }
71
+ var normalize3 = (x, y, z) => {
72
+ const length = Math.sqrt(x * x + y * y + z * z);
73
+ const s = 1 / (length || 1);
74
+ return [x * s, y * s, z * s];
75
+ };
76
+ function box(width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1) {
77
+ const parameters = { width, height, depth, widthSegments, heightSegments, depthSegments };
78
+ widthSegments = Math.floor(widthSegments);
79
+ heightSegments = Math.floor(heightSegments);
80
+ depthSegments = Math.floor(depthSegments);
81
+ const indices = [];
82
+ const vertices = [];
83
+ const normals = [];
84
+ const uvs = [];
85
+ const groups = [];
86
+ let numberOfVertices = 0;
87
+ let groupStart = 0;
88
+ const buildPlane = (u, v, w, udir, vdir, width2, height2, depth2, gridX, gridY, materialIndex) => {
89
+ const segmentWidth = width2 / gridX;
90
+ const segmentHeight = height2 / gridY;
91
+ const widthHalf = width2 / 2;
92
+ const heightHalf = height2 / 2;
93
+ const depthHalf = depth2 / 2;
94
+ const gridX1 = gridX + 1;
95
+ const gridY1 = gridY + 1;
96
+ let vertexCounter = 0;
97
+ let groupCount = 0;
98
+ const vector = { x: 0, y: 0, z: 0 };
99
+ for (let iy = 0; iy < gridY1; iy++) {
100
+ const y = iy * segmentHeight - heightHalf;
101
+ for (let ix = 0; ix < gridX1; ix++) {
102
+ const x = ix * segmentWidth - widthHalf;
103
+ vector[u] = x * udir;
104
+ vector[v] = y * vdir;
105
+ vector[w] = depthHalf;
106
+ vertices.push(vector.x, vector.y, vector.z);
107
+ vector[u] = 0;
108
+ vector[v] = 0;
109
+ vector[w] = depth2 > 0 ? 1 : -1;
110
+ normals.push(vector.x, vector.y, vector.z);
111
+ uvs.push(ix / gridX);
112
+ uvs.push(1 - iy / gridY);
113
+ vertexCounter += 1;
114
+ }
115
+ }
116
+ for (let iy = 0; iy < gridY; iy++) {
117
+ for (let ix = 0; ix < gridX; ix++) {
118
+ const a = numberOfVertices + ix + gridX1 * iy;
119
+ const b = numberOfVertices + ix + gridX1 * (iy + 1);
120
+ const c = numberOfVertices + (ix + 1) + gridX1 * (iy + 1);
121
+ const d = numberOfVertices + (ix + 1) + gridX1 * iy;
122
+ indices.push(a, b, d);
123
+ indices.push(b, c, d);
124
+ groupCount += 6;
125
+ }
126
+ }
127
+ groups.push({ start: groupStart, count: groupCount, materialIndex });
128
+ groupStart += groupCount;
129
+ numberOfVertices += vertexCounter;
130
+ };
131
+ buildPlane("z", "y", "x", -1, -1, depth, height, width, depthSegments, heightSegments, 0);
132
+ buildPlane("z", "y", "x", 1, -1, depth, height, -width, depthSegments, heightSegments, 1);
133
+ buildPlane("x", "z", "y", 1, 1, width, depth, height, widthSegments, depthSegments, 2);
134
+ buildPlane("x", "z", "y", 1, -1, width, depth, -height, widthSegments, depthSegments, 3);
135
+ buildPlane("x", "y", "z", 1, -1, width, height, depth, widthSegments, heightSegments, 4);
136
+ buildPlane("x", "y", "z", -1, -1, width, height, -depth, widthSegments, heightSegments, 5);
137
+ return pack("BoxGeometry", parameters, { vertices, normals, uvs, indices, groups });
138
+ }
139
+ function cylinderArrays(radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength) {
140
+ radialSegments = Math.floor(radialSegments);
141
+ heightSegments = Math.floor(heightSegments);
142
+ const indices = [];
143
+ const vertices = [];
144
+ const normals = [];
145
+ const uvs = [];
146
+ const groups = [];
147
+ let index = 0;
148
+ const indexArray2 = [];
149
+ const halfHeight = height / 2;
150
+ let groupStart = 0;
151
+ const generateTorso = () => {
152
+ let groupCount = 0;
153
+ const slope = (radiusBottom - radiusTop) / height;
154
+ for (let y = 0; y <= heightSegments; y++) {
155
+ const indexRow = [];
156
+ const v = y / heightSegments;
157
+ const radius = v * (radiusBottom - radiusTop) + radiusTop;
158
+ for (let x = 0; x <= radialSegments; x++) {
159
+ const u = x / radialSegments;
160
+ const theta = u * thetaLength + thetaStart;
161
+ const sinTheta = Math.sin(theta);
162
+ const cosTheta = Math.cos(theta);
163
+ vertices.push(radius * sinTheta, -v * height + halfHeight, radius * cosTheta);
164
+ const n = normalize3(sinTheta, slope, cosTheta);
165
+ normals.push(n[0], n[1], n[2]);
166
+ uvs.push(u, 1 - v);
167
+ indexRow.push(index++);
168
+ }
169
+ indexArray2.push(indexRow);
170
+ }
171
+ for (let x = 0; x < radialSegments; x++) {
172
+ for (let y = 0; y < heightSegments; y++) {
173
+ const a = indexArray2[y][x];
174
+ const b = indexArray2[y + 1][x];
175
+ const c = indexArray2[y + 1][x + 1];
176
+ const d = indexArray2[y][x + 1];
177
+ indices.push(a, b, d);
178
+ indices.push(b, c, d);
179
+ groupCount += 6;
180
+ }
181
+ }
182
+ groups.push({ start: groupStart, count: groupCount, materialIndex: 0 });
183
+ groupStart += groupCount;
184
+ };
185
+ const generateCap = (top) => {
186
+ const centerIndexStart = index;
187
+ let groupCount = 0;
188
+ const radius = top === true ? radiusTop : radiusBottom;
189
+ const sign2 = top === true ? 1 : -1;
190
+ for (let x = 1; x <= radialSegments; x++) {
191
+ vertices.push(0, halfHeight * sign2, 0);
192
+ normals.push(0, sign2, 0);
193
+ uvs.push(0.5, 0.5);
194
+ index++;
195
+ }
196
+ const centerIndexEnd = index;
197
+ for (let x = 0; x <= radialSegments; x++) {
198
+ const u = x / radialSegments;
199
+ const theta = u * thetaLength + thetaStart;
200
+ const cosTheta = Math.cos(theta);
201
+ const sinTheta = Math.sin(theta);
202
+ vertices.push(radius * sinTheta, halfHeight * sign2, radius * cosTheta);
203
+ normals.push(0, sign2, 0);
204
+ uvs.push(cosTheta * 0.5 + 0.5, sinTheta * 0.5 * sign2 + 0.5);
205
+ index++;
206
+ }
207
+ for (let x = 0; x < radialSegments; x++) {
208
+ const c = centerIndexStart + x;
209
+ const i = centerIndexEnd + x;
210
+ if (top === true) indices.push(i, i + 1, c);
211
+ else indices.push(i + 1, i, c);
212
+ groupCount += 3;
213
+ }
214
+ groups.push({ start: groupStart, count: groupCount, materialIndex: top === true ? 1 : 2 });
215
+ groupStart += groupCount;
216
+ };
217
+ generateTorso();
218
+ if (openEnded === false) {
219
+ if (radiusTop > 0) generateCap(true);
220
+ if (radiusBottom > 0) generateCap(false);
221
+ }
222
+ return { vertices, normals, uvs, indices, groups };
223
+ }
224
+ function cylinder(radiusTop = 1, radiusBottom = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2) {
225
+ const parameters = { radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength };
226
+ return pack("CylinderGeometry", parameters, cylinderArrays(radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength));
227
+ }
228
+ function cone(radius = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2) {
229
+ const parameters = { radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength };
230
+ return pack("ConeGeometry", parameters, cylinderArrays(0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength));
231
+ }
232
+ function sphere(radius = 1, widthSegments = 32, heightSegments = 16, phiStart = 0, phiLength = Math.PI * 2, thetaStart = 0, thetaLength = Math.PI) {
233
+ const parameters = { radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength };
234
+ widthSegments = Math.max(3, Math.floor(widthSegments));
235
+ heightSegments = Math.max(2, Math.floor(heightSegments));
236
+ const thetaEnd = Math.min(thetaStart + thetaLength, Math.PI);
237
+ let index = 0;
238
+ const grid = [];
239
+ const indices = [];
240
+ const vertices = [];
241
+ const normals = [];
242
+ const uvs = [];
243
+ for (let iy = 0; iy <= heightSegments; iy++) {
244
+ const verticesRow = [];
245
+ const v = iy / heightSegments;
246
+ let uOffset = 0;
247
+ if (iy === 0 && thetaStart === 0) uOffset = 0.5 / widthSegments;
248
+ else if (iy === heightSegments && thetaEnd === Math.PI) uOffset = -0.5 / widthSegments;
249
+ for (let ix = 0; ix <= widthSegments; ix++) {
250
+ const u = ix / widthSegments;
251
+ const x = -radius * Math.cos(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength);
252
+ const y = radius * Math.cos(thetaStart + v * thetaLength);
253
+ const z = radius * Math.sin(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength);
254
+ vertices.push(x, y, z);
255
+ const n = normalize3(x, y, z);
256
+ normals.push(n[0], n[1], n[2]);
257
+ uvs.push(u + uOffset, 1 - v);
258
+ verticesRow.push(index++);
259
+ }
260
+ grid.push(verticesRow);
261
+ }
262
+ for (let iy = 0; iy < heightSegments; iy++) {
263
+ for (let ix = 0; ix < widthSegments; ix++) {
264
+ const a = grid[iy][ix + 1];
265
+ const b = grid[iy][ix];
266
+ const c = grid[iy + 1][ix];
267
+ const d = grid[iy + 1][ix + 1];
268
+ if (iy !== 0 || thetaStart > 0) indices.push(a, b, d);
269
+ if (iy !== heightSegments - 1 || thetaEnd < Math.PI) indices.push(b, c, d);
270
+ }
271
+ }
272
+ return pack("SphereGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
273
+ }
274
+ function plane(width = 1, height = 1, widthSegments = 1, heightSegments = 1) {
275
+ const parameters = { width, height, widthSegments, heightSegments };
276
+ const widthHalf = width / 2;
277
+ const heightHalf = height / 2;
278
+ const gridX = Math.floor(widthSegments);
279
+ const gridY = Math.floor(heightSegments);
280
+ const gridX1 = gridX + 1;
281
+ const gridY1 = gridY + 1;
282
+ const segmentWidth = width / gridX;
283
+ const segmentHeight = height / gridY;
284
+ const indices = [];
285
+ const vertices = [];
286
+ const normals = [];
287
+ const uvs = [];
288
+ for (let iy = 0; iy < gridY1; iy++) {
289
+ const y = iy * segmentHeight - heightHalf;
290
+ for (let ix = 0; ix < gridX1; ix++) {
291
+ const x = ix * segmentWidth - widthHalf;
292
+ vertices.push(x, -y, 0);
293
+ normals.push(0, 0, 1);
294
+ uvs.push(ix / gridX);
295
+ uvs.push(1 - iy / gridY);
296
+ }
297
+ }
298
+ for (let iy = 0; iy < gridY; iy++) {
299
+ for (let ix = 0; ix < gridX; ix++) {
300
+ const a = ix + gridX1 * iy;
301
+ const b = ix + gridX1 * (iy + 1);
302
+ const c = ix + 1 + gridX1 * (iy + 1);
303
+ const d = ix + 1 + gridX1 * iy;
304
+ indices.push(a, b, d);
305
+ indices.push(b, c, d);
306
+ }
307
+ }
308
+ return pack("PlaneGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
309
+ }
310
+ function circle(radius = 1, segments = 32, thetaStart = 0, thetaLength = Math.PI * 2) {
311
+ const parameters = { radius, segments, thetaStart, thetaLength };
312
+ segments = Math.max(3, segments);
313
+ const indices = [];
314
+ const vertices = [];
315
+ const normals = [];
316
+ const uvs = [];
317
+ vertices.push(0, 0, 0);
318
+ normals.push(0, 0, 1);
319
+ uvs.push(0.5, 0.5);
320
+ for (let s = 0, i = 3; s <= segments; s++, i += 3) {
321
+ const segment = thetaStart + s / segments * thetaLength;
322
+ vertices.push(radius * Math.cos(segment), radius * Math.sin(segment), 0);
323
+ normals.push(0, 0, 1);
324
+ uvs.push((vertices[i] / radius + 1) / 2, (vertices[i + 1] / radius + 1) / 2);
325
+ }
326
+ for (let i = 1; i <= segments; i++) indices.push(i, i + 1, 0);
327
+ return pack("CircleGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
328
+ }
329
+ function ring(innerRadius = 0.5, outerRadius = 1, thetaSegments = 32, phiSegments = 1, thetaStart = 0, thetaLength = Math.PI * 2) {
330
+ const parameters = { innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength };
331
+ thetaSegments = Math.max(3, thetaSegments);
332
+ phiSegments = Math.max(1, phiSegments);
333
+ const indices = [];
334
+ const vertices = [];
335
+ const normals = [];
336
+ const uvs = [];
337
+ let radius = innerRadius;
338
+ const radiusStep = (outerRadius - innerRadius) / phiSegments;
339
+ for (let j = 0; j <= phiSegments; j++) {
340
+ for (let i = 0; i <= thetaSegments; i++) {
341
+ const segment = thetaStart + i / thetaSegments * thetaLength;
342
+ const x = radius * Math.cos(segment);
343
+ const y = radius * Math.sin(segment);
344
+ vertices.push(x, y, 0);
345
+ normals.push(0, 0, 1);
346
+ uvs.push((x / outerRadius + 1) / 2, (y / outerRadius + 1) / 2);
347
+ }
348
+ radius += radiusStep;
349
+ }
350
+ for (let j = 0; j < phiSegments; j++) {
351
+ const thetaSegmentLevel = j * (thetaSegments + 1);
352
+ for (let i = 0; i < thetaSegments; i++) {
353
+ const segment = i + thetaSegmentLevel;
354
+ const a = segment;
355
+ const b = segment + thetaSegments + 1;
356
+ const c = segment + thetaSegments + 2;
357
+ const d = segment + 1;
358
+ indices.push(a, b, d);
359
+ indices.push(b, c, d);
360
+ }
361
+ }
362
+ return pack("RingGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
363
+ }
364
+ function lathe(points, segments = 12, phiStart = 0, phiLength = Math.PI * 2) {
365
+ const parameters = { points: points.map(([x, y]) => ({ x, y })), segments, phiStart, phiLength };
366
+ segments = Math.floor(segments);
367
+ phiLength = Math.max(0, Math.min(Math.PI * 2, phiLength));
368
+ const indices = [];
369
+ const vertices = [];
370
+ const uvs = [];
371
+ const initNormals = [];
372
+ const normals = [];
373
+ const inverseSegments = 1 / segments;
374
+ const normal = { x: 0, y: 0, z: 0 };
375
+ const curNormal = { x: 0, y: 0, z: 0 };
376
+ const prevNormal = { x: 0, y: 0, z: 0 };
377
+ let dx = 0;
378
+ let dy = 0;
379
+ const last = points.length - 1;
380
+ const px = (j) => points[j][0];
381
+ const py = (j) => points[j][1];
382
+ const setNormalized = () => {
383
+ const n = normalize3(normal.x, normal.y, normal.z);
384
+ normal.x = n[0];
385
+ normal.y = n[1];
386
+ normal.z = n[2];
387
+ };
388
+ for (let j = 0; j <= last; j++) {
389
+ if (j === 0) {
390
+ dx = px(j + 1) - px(j);
391
+ dy = py(j + 1) - py(j);
392
+ normal.x = dy * 1;
393
+ normal.y = -dx;
394
+ normal.z = dy * 0;
395
+ prevNormal.x = normal.x;
396
+ prevNormal.y = normal.y;
397
+ prevNormal.z = normal.z;
398
+ setNormalized();
399
+ initNormals.push(normal.x, normal.y, normal.z);
400
+ } else if (j === last) {
401
+ initNormals.push(prevNormal.x, prevNormal.y, prevNormal.z);
402
+ } else {
403
+ dx = px(j + 1) - px(j);
404
+ dy = py(j + 1) - py(j);
405
+ normal.x = dy * 1;
406
+ normal.y = -dx;
407
+ normal.z = dy * 0;
408
+ curNormal.x = normal.x;
409
+ curNormal.y = normal.y;
410
+ curNormal.z = normal.z;
411
+ normal.x += prevNormal.x;
412
+ normal.y += prevNormal.y;
413
+ normal.z += prevNormal.z;
414
+ setNormalized();
415
+ initNormals.push(normal.x, normal.y, normal.z);
416
+ prevNormal.x = curNormal.x;
417
+ prevNormal.y = curNormal.y;
418
+ prevNormal.z = curNormal.z;
419
+ }
420
+ }
421
+ for (let i = 0; i <= segments; i++) {
422
+ const phi = phiStart + i * inverseSegments * phiLength;
423
+ const sin = Math.sin(phi);
424
+ const cos = Math.cos(phi);
425
+ for (let j = 0; j <= last; j++) {
426
+ vertices.push(px(j) * sin, py(j), px(j) * cos);
427
+ uvs.push(i / segments, j / (points.length - 1));
428
+ const x = initNormals[3 * j + 0] * sin;
429
+ const y = initNormals[3 * j + 1];
430
+ const z = initNormals[3 * j + 0] * cos;
431
+ normals.push(x, y, z);
432
+ }
433
+ }
434
+ for (let i = 0; i < segments; i++) {
435
+ for (let j = 0; j < points.length - 1; j++) {
436
+ const base = j + i * points.length;
437
+ const a = base;
438
+ const b = base + points.length;
439
+ const c = base + points.length + 1;
440
+ const d = base + 1;
441
+ indices.push(a, b, d);
442
+ indices.push(c, d, b);
443
+ }
444
+ }
445
+ return pack("LatheGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
446
+ }
447
+ function torus(radius = 1, tube2 = 0.4, radialSegments = 12, tubularSegments = 48, arc = Math.PI * 2) {
448
+ const parameters = { radius, tube: tube2, radialSegments, tubularSegments, arc };
449
+ radialSegments = Math.floor(radialSegments);
450
+ tubularSegments = Math.floor(tubularSegments);
451
+ const indices = [];
452
+ const vertices = [];
453
+ const normals = [];
454
+ const uvs = [];
455
+ for (let j = 0; j <= radialSegments; j++) {
456
+ for (let i = 0; i <= tubularSegments; i++) {
457
+ const u = i / tubularSegments * arc;
458
+ const v = j / radialSegments * Math.PI * 2;
459
+ const x = (radius + tube2 * Math.cos(v)) * Math.cos(u);
460
+ const y = (radius + tube2 * Math.cos(v)) * Math.sin(u);
461
+ const z = tube2 * Math.sin(v);
462
+ vertices.push(x, y, z);
463
+ const cx = radius * Math.cos(u);
464
+ const cy = radius * Math.sin(u);
465
+ const n = normalize3(x - cx, y - cy, z - 0);
466
+ normals.push(n[0], n[1], n[2]);
467
+ uvs.push(i / tubularSegments);
468
+ uvs.push(j / radialSegments);
469
+ }
470
+ }
471
+ for (let j = 1; j <= radialSegments; j++) {
472
+ for (let i = 1; i <= tubularSegments; i++) {
473
+ const a = (tubularSegments + 1) * j + i - 1;
474
+ const b = (tubularSegments + 1) * (j - 1) + i - 1;
475
+ const c = (tubularSegments + 1) * (j - 1) + i;
476
+ const d = (tubularSegments + 1) * j + i;
477
+ indices.push(a, b, d);
478
+ indices.push(b, c, d);
479
+ }
480
+ }
481
+ return pack("TorusGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
482
+ }
483
+ function difference(size, subtract) {
484
+ const [w, h, d] = size;
485
+ const hw = w / 2;
486
+ const hh = h / 2;
487
+ const hd = d / 2;
488
+ 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);
489
+ const cuts = (lo, hi, values) => [.../* @__PURE__ */ new Set([lo, hi, ...values.filter((v) => v > lo && v < hi)])].sort((a, b) => a - b);
490
+ const xs = cuts(-hw, hw, holes.flatMap((hole) => [hole.x0, hole.x1]));
491
+ const ys = cuts(-hh, hh, holes.flatMap((hole) => [hole.y0, hole.y1]));
492
+ const nx = xs.length - 1;
493
+ const ny = ys.length - 1;
494
+ const solid = (i, j) => {
495
+ if (i < 0 || j < 0 || i >= nx || j >= ny) return false;
496
+ const cx = (xs[i] + xs[i + 1]) / 2;
497
+ const cy = (ys[j] + ys[j + 1]) / 2;
498
+ return !holes.some((hole) => cx > hole.x0 && cx < hole.x1 && cy > hole.y0 && cy < hole.y1);
499
+ };
500
+ const positions = [];
501
+ const normals = [];
502
+ const uvs = [];
503
+ const indices = [];
504
+ const quad = (corners, normal, uv) => {
505
+ const base = positions.length / 3;
506
+ for (let k = 0; k < 4; k++) {
507
+ positions.push(...corners[k]);
508
+ normals.push(...normal);
509
+ uvs.push(...uv[k]);
510
+ }
511
+ indices.push(base, base + 1, base + 2, base, base + 2, base + 3);
512
+ };
513
+ const fu = (x) => (x + hw) / w;
514
+ const fv = (y) => (y + hh) / h;
515
+ for (let j = 0; j < ny; j++) {
516
+ for (let i = 0; i < nx; i++) {
517
+ if (!solid(i, j)) continue;
518
+ const x0 = xs[i];
519
+ const x1 = xs[i + 1];
520
+ const y0 = ys[j];
521
+ const y1 = ys[j + 1];
522
+ 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)]]);
523
+ 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)]]);
524
+ const zu = (z) => (z + hd) / d;
525
+ 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)]]);
526
+ 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)]]);
527
+ 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)]]);
528
+ 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)]]);
529
+ }
530
+ }
531
+ return {
532
+ type: "BufferGeometry",
533
+ parameters: { size: [w, h, d], subtract: holes.map((hole) => ({ min: [hole.x0, hole.y0], max: [hole.x1, hole.y1] })) },
534
+ positions: new Float32Array(positions),
535
+ normals: new Float32Array(normals),
536
+ uvs: new Float32Array(uvs),
537
+ indices: indexArray(indices),
538
+ groups: []
539
+ };
540
+ }
541
+ var Earcut = {
542
+ triangulate: function(data, holeIndices, dim = 2) {
543
+ const hasHoles = holeIndices && holeIndices.length;
544
+ const outerLen = hasHoles ? holeIndices[0] * dim : data.length;
545
+ let outerNode = linkedList(data, 0, outerLen, dim, true);
546
+ const triangles = [];
547
+ if (!outerNode || outerNode.next === outerNode.prev) return triangles;
548
+ let minX, minY, maxX, maxY, x, y, invSize;
549
+ if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim);
550
+ if (data.length > 80 * dim) {
551
+ minX = maxX = data[0];
552
+ minY = maxY = data[1];
553
+ for (let i = dim; i < outerLen; i += dim) {
554
+ x = data[i];
555
+ y = data[i + 1];
556
+ if (x < minX) minX = x;
557
+ if (y < minY) minY = y;
558
+ if (x > maxX) maxX = x;
559
+ if (y > maxY) maxY = y;
560
+ }
561
+ invSize = Math.max(maxX - minX, maxY - minY);
562
+ invSize = invSize !== 0 ? 32767 / invSize : 0;
563
+ }
564
+ earcutLinked(outerNode, triangles, dim, minX, minY, invSize, 0);
565
+ return triangles;
566
+ }
567
+ };
568
+ function linkedList(data, start, end, dim, clockwise) {
569
+ let i, last;
570
+ if (clockwise === signedArea(data, start, end, dim) > 0) {
571
+ for (i = start; i < end; i += dim) last = insertNode(i, data[i], data[i + 1], last);
572
+ } else {
573
+ for (i = end - dim; i >= start; i -= dim) last = insertNode(i, data[i], data[i + 1], last);
574
+ }
575
+ if (last && equals(last, last.next)) {
576
+ removeNode(last);
577
+ last = last.next;
578
+ }
579
+ return last;
580
+ }
581
+ function filterPoints(start, end) {
582
+ if (!start) return start;
583
+ if (!end) end = start;
584
+ let p = start, again;
585
+ do {
586
+ again = false;
587
+ if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) {
588
+ removeNode(p);
589
+ p = end = p.prev;
590
+ if (p === p.next) break;
591
+ again = true;
592
+ } else {
593
+ p = p.next;
594
+ }
595
+ } while (again || p !== end);
596
+ return end;
597
+ }
598
+ function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) {
599
+ if (!ear) return;
600
+ if (!pass && invSize) indexCurve(ear, minX, minY, invSize);
601
+ let stop = ear, prev, next;
602
+ while (ear.prev !== ear.next) {
603
+ prev = ear.prev;
604
+ next = ear.next;
605
+ if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) {
606
+ triangles.push(prev.i / dim | 0);
607
+ triangles.push(ear.i / dim | 0);
608
+ triangles.push(next.i / dim | 0);
609
+ removeNode(ear);
610
+ ear = next.next;
611
+ stop = next.next;
612
+ continue;
613
+ }
614
+ ear = next;
615
+ if (ear === stop) {
616
+ if (!pass) {
617
+ earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1);
618
+ } else if (pass === 1) {
619
+ ear = cureLocalIntersections(filterPoints(ear), triangles, dim);
620
+ earcutLinked(ear, triangles, dim, minX, minY, invSize, 2);
621
+ } else if (pass === 2) {
622
+ splitEarcut(ear, triangles, dim, minX, minY, invSize);
623
+ }
624
+ break;
625
+ }
626
+ }
627
+ }
628
+ function isEar(ear) {
629
+ const a = ear.prev, b = ear, c = ear.next;
630
+ if (area(a, b, c) >= 0) return false;
631
+ const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
632
+ 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;
633
+ let p = c.next;
634
+ while (p !== a) {
635
+ 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;
636
+ p = p.next;
637
+ }
638
+ return true;
639
+ }
640
+ function isEarHashed(ear, minX, minY, invSize) {
641
+ const a = ear.prev, b = ear, c = ear.next;
642
+ if (area(a, b, c) >= 0) return false;
643
+ const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
644
+ 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;
645
+ const minZ = zOrder(x0, y0, minX, minY, invSize), maxZ = zOrder(x1, y1, minX, minY, invSize);
646
+ let p = ear.prevZ, n = ear.nextZ;
647
+ while (p && p.z >= minZ && n && n.z <= maxZ) {
648
+ 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;
649
+ p = p.prevZ;
650
+ 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;
651
+ n = n.nextZ;
652
+ }
653
+ while (p && p.z >= minZ) {
654
+ 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;
655
+ p = p.prevZ;
656
+ }
657
+ while (n && n.z <= maxZ) {
658
+ 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;
659
+ n = n.nextZ;
660
+ }
661
+ return true;
662
+ }
663
+ function cureLocalIntersections(start, triangles, dim) {
664
+ let p = start;
665
+ do {
666
+ const a = p.prev, b = p.next.next;
667
+ if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) {
668
+ triangles.push(a.i / dim | 0);
669
+ triangles.push(p.i / dim | 0);
670
+ triangles.push(b.i / dim | 0);
671
+ removeNode(p);
672
+ removeNode(p.next);
673
+ p = start = b;
674
+ }
675
+ p = p.next;
676
+ } while (p !== start);
677
+ return filterPoints(p);
678
+ }
679
+ function splitEarcut(start, triangles, dim, minX, minY, invSize) {
680
+ let a = start;
681
+ do {
682
+ let b = a.next.next;
683
+ while (b !== a.prev) {
684
+ if (a.i !== b.i && isValidDiagonal(a, b)) {
685
+ let c = splitPolygon(a, b);
686
+ a = filterPoints(a, a.next);
687
+ c = filterPoints(c, c.next);
688
+ earcutLinked(a, triangles, dim, minX, minY, invSize, 0);
689
+ earcutLinked(c, triangles, dim, minX, minY, invSize, 0);
690
+ return;
691
+ }
692
+ b = b.next;
693
+ }
694
+ a = a.next;
695
+ } while (a !== start);
696
+ }
697
+ function eliminateHoles(data, holeIndices, outerNode, dim) {
698
+ const queue = [];
699
+ let i, len2, start, end, list2;
700
+ for (i = 0, len2 = holeIndices.length; i < len2; i++) {
701
+ start = holeIndices[i] * dim;
702
+ end = i < len2 - 1 ? holeIndices[i + 1] * dim : data.length;
703
+ list2 = linkedList(data, start, end, dim, false);
704
+ if (list2 === list2.next) list2.steiner = true;
705
+ queue.push(getLeftmost(list2));
706
+ }
707
+ queue.sort(compareX);
708
+ for (i = 0; i < queue.length; i++) {
709
+ outerNode = eliminateHole(queue[i], outerNode);
710
+ }
711
+ return outerNode;
712
+ }
713
+ function compareX(a, b) {
714
+ return a.x - b.x;
715
+ }
716
+ function eliminateHole(hole, outerNode) {
717
+ const bridge = findHoleBridge(hole, outerNode);
718
+ if (!bridge) {
719
+ return outerNode;
720
+ }
721
+ const bridgeReverse = splitPolygon(bridge, hole);
722
+ filterPoints(bridgeReverse, bridgeReverse.next);
723
+ return filterPoints(bridge, bridge.next);
724
+ }
725
+ function findHoleBridge(hole, outerNode) {
726
+ let p = outerNode, qx = -Infinity, m;
727
+ const hx = hole.x, hy = hole.y;
728
+ do {
729
+ if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) {
730
+ const x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y);
731
+ if (x <= hx && x > qx) {
732
+ qx = x;
733
+ m = p.x < p.next.x ? p : p.next;
734
+ if (x === hx) return m;
735
+ }
736
+ }
737
+ p = p.next;
738
+ } while (p !== outerNode);
739
+ if (!m) return null;
740
+ const stop = m, mx = m.x, my = m.y;
741
+ let tanMin = Infinity, tan;
742
+ p = m;
743
+ do {
744
+ 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)) {
745
+ tan = Math.abs(hy - p.y) / (hx - p.x);
746
+ if (locallyInside(p, hole) && (tan < tanMin || tan === tanMin && (p.x > m.x || p.x === m.x && sectorContainsSector(m, p)))) {
747
+ m = p;
748
+ tanMin = tan;
749
+ }
750
+ }
751
+ p = p.next;
752
+ } while (p !== stop);
753
+ return m;
754
+ }
755
+ function sectorContainsSector(m, p) {
756
+ return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0;
757
+ }
758
+ function indexCurve(start, minX, minY, invSize) {
759
+ let p = start;
760
+ do {
761
+ if (p.z === 0) p.z = zOrder(p.x, p.y, minX, minY, invSize);
762
+ p.prevZ = p.prev;
763
+ p.nextZ = p.next;
764
+ p = p.next;
765
+ } while (p !== start);
766
+ p.prevZ.nextZ = null;
767
+ p.prevZ = null;
768
+ sortLinked(p);
769
+ }
770
+ function sortLinked(list2) {
771
+ let i, p, q, e, tail, numMerges, pSize, qSize, inSize = 1;
772
+ do {
773
+ p = list2;
774
+ list2 = null;
775
+ tail = null;
776
+ numMerges = 0;
777
+ while (p) {
778
+ numMerges++;
779
+ q = p;
780
+ pSize = 0;
781
+ for (i = 0; i < inSize; i++) {
782
+ pSize++;
783
+ q = q.nextZ;
784
+ if (!q) break;
785
+ }
786
+ qSize = inSize;
787
+ while (pSize > 0 || qSize > 0 && q) {
788
+ if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) {
789
+ e = p;
790
+ p = p.nextZ;
791
+ pSize--;
792
+ } else {
793
+ e = q;
794
+ q = q.nextZ;
795
+ qSize--;
796
+ }
797
+ if (tail) tail.nextZ = e;
798
+ else list2 = e;
799
+ e.prevZ = tail;
800
+ tail = e;
801
+ }
802
+ p = q;
803
+ }
804
+ tail.nextZ = null;
805
+ inSize *= 2;
806
+ } while (numMerges > 1);
807
+ return list2;
808
+ }
809
+ function zOrder(x, y, minX, minY, invSize) {
810
+ x = (x - minX) * invSize | 0;
811
+ y = (y - minY) * invSize | 0;
812
+ x = (x | x << 8) & 16711935;
813
+ x = (x | x << 4) & 252645135;
814
+ x = (x | x << 2) & 858993459;
815
+ x = (x | x << 1) & 1431655765;
816
+ y = (y | y << 8) & 16711935;
817
+ y = (y | y << 4) & 252645135;
818
+ y = (y | y << 2) & 858993459;
819
+ y = (y | y << 1) & 1431655765;
820
+ return x | y << 1;
821
+ }
822
+ function getLeftmost(start) {
823
+ let p = start, leftmost = start;
824
+ do {
825
+ if (p.x < leftmost.x || p.x === leftmost.x && p.y < leftmost.y) leftmost = p;
826
+ p = p.next;
827
+ } while (p !== start);
828
+ return leftmost;
829
+ }
830
+ function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) {
831
+ 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);
832
+ }
833
+ function isValidDiagonal(a, b) {
834
+ return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && // dones't intersect other edges
835
+ (locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible
836
+ (area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors
837
+ equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0);
838
+ }
839
+ function area(p, q, r) {
840
+ return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
841
+ }
842
+ function equals(p1, p2) {
843
+ return p1.x === p2.x && p1.y === p2.y;
844
+ }
845
+ function intersects(p1, q1, p2, q2) {
846
+ const o1 = sign(area(p1, q1, p2));
847
+ const o2 = sign(area(p1, q1, q2));
848
+ const o3 = sign(area(p2, q2, p1));
849
+ const o4 = sign(area(p2, q2, q1));
850
+ if (o1 !== o2 && o3 !== o4) return true;
851
+ if (o1 === 0 && onSegment(p1, p2, q1)) return true;
852
+ if (o2 === 0 && onSegment(p1, q2, q1)) return true;
853
+ if (o3 === 0 && onSegment(p2, p1, q2)) return true;
854
+ if (o4 === 0 && onSegment(p2, q1, q2)) return true;
855
+ return false;
856
+ }
857
+ function onSegment(p, q, r) {
858
+ 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);
859
+ }
860
+ function sign(num2) {
861
+ return num2 > 0 ? 1 : num2 < 0 ? -1 : 0;
862
+ }
863
+ function intersectsPolygon(a, b) {
864
+ let p = a;
865
+ do {
866
+ 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;
867
+ p = p.next;
868
+ } while (p !== a);
869
+ return false;
870
+ }
871
+ function locallyInside(a, b) {
872
+ 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;
873
+ }
874
+ function middleInside(a, b) {
875
+ let p = a, inside = false;
876
+ const px = (a.x + b.x) / 2, py = (a.y + b.y) / 2;
877
+ do {
878
+ 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)
879
+ inside = !inside;
880
+ p = p.next;
881
+ } while (p !== a);
882
+ return inside;
883
+ }
884
+ function splitPolygon(a, b) {
885
+ 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;
886
+ a.next = b;
887
+ b.prev = a;
888
+ a2.next = an;
889
+ an.prev = a2;
890
+ b2.next = a2;
891
+ a2.prev = b2;
892
+ bp.next = b2;
893
+ b2.prev = bp;
894
+ return b2;
895
+ }
896
+ function insertNode(i, x, y, last) {
897
+ const p = new Node(i, x, y);
898
+ if (!last) {
899
+ p.prev = p;
900
+ p.next = p;
901
+ } else {
902
+ p.next = last.next;
903
+ p.prev = last;
904
+ last.next.prev = p;
905
+ last.next = p;
906
+ }
907
+ return p;
908
+ }
909
+ function removeNode(p) {
910
+ p.next.prev = p.prev;
911
+ p.prev.next = p.next;
912
+ if (p.prevZ) p.prevZ.nextZ = p.nextZ;
913
+ if (p.nextZ) p.nextZ.prevZ = p.prevZ;
914
+ }
915
+ function Node(i, x, y) {
916
+ this.i = i;
917
+ this.x = x;
918
+ this.y = y;
919
+ this.prev = null;
920
+ this.next = null;
921
+ this.z = 0;
922
+ this.prevZ = null;
923
+ this.nextZ = null;
924
+ this.steiner = false;
925
+ }
926
+ function signedArea(data, start, end, dim) {
927
+ let sum2 = 0;
928
+ for (let i = start, j = end - dim; i < end; i += dim) {
929
+ sum2 += (data[j] - data[i]) * (data[i + 1] + data[j + 1]);
930
+ j = i;
931
+ }
932
+ return sum2;
933
+ }
934
+ var equals2 = (a, b) => a.x === b.x && a.y === b.y;
935
+ 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 };
936
+ function pathPoints(points) {
937
+ const out = [];
938
+ let last;
939
+ for (let i = 1; i < points.length; i++) {
940
+ const v1 = { x: points[i - 1][0], y: points[i - 1][1] };
941
+ const v2 = { x: points[i][0], y: points[i][1] };
942
+ for (let d = 0; d <= 1; d++) {
943
+ const point = linePoint(v1, v2, d / 1);
944
+ if (last && equals2(last, point)) continue;
945
+ out.push(point);
946
+ last = point;
947
+ }
948
+ }
949
+ return out;
950
+ }
951
+ function area2(contour) {
952
+ const n = contour.length;
953
+ let a = 0;
954
+ for (let p = n - 1, q = 0; q < n; p = q++) a += contour[p].x * contour[q].y - contour[q].x * contour[p].y;
955
+ return a * 0.5;
956
+ }
957
+ var isClockWise = (pts) => area2(pts) < 0;
958
+ function removeDupEndPts(points) {
959
+ const l = points.length;
960
+ if (l > 2 && equals2(points[l - 1], points[0])) points.pop();
961
+ }
962
+ function triangulateShape(contour, holes) {
963
+ const vertices = [];
964
+ const holeIndices = [];
965
+ const faces = [];
966
+ removeDupEndPts(contour);
967
+ for (const p of contour) vertices.push(p.x, p.y);
968
+ let holeIndex = contour.length;
969
+ holes.forEach(removeDupEndPts);
970
+ for (let i = 0; i < holes.length; i++) {
971
+ holeIndices.push(holeIndex);
972
+ holeIndex += holes[i].length;
973
+ for (const p of holes[i]) vertices.push(p.x, p.y);
974
+ }
975
+ const triangles = Earcut.triangulate(vertices, holeIndices);
976
+ for (let i = 0; i < triangles.length; i += 3) faces.push(triangles.slice(i, i + 3));
977
+ return faces;
978
+ }
979
+ function computeVertexNormals(positions, indices) {
980
+ const normals = new Float32Array(positions.length);
981
+ const get = (array, i) => [array[i * 3], array[i * 3 + 1], array[i * 3 + 2]];
982
+ const face = (vA, vB, vC) => {
983
+ const pA = get(positions, vA);
984
+ const pB = get(positions, vB);
985
+ const pC = get(positions, vC);
986
+ const cb = [pC[0] - pB[0], pC[1] - pB[1], pC[2] - pB[2]];
987
+ const ab = [pA[0] - pB[0], pA[1] - pB[1], pA[2] - pB[2]];
988
+ 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]];
989
+ };
990
+ const set = (i, x, y, z) => {
991
+ normals[i * 3] = x;
992
+ normals[i * 3 + 1] = y;
993
+ normals[i * 3 + 2] = z;
994
+ };
995
+ if (indices) {
996
+ for (let i = 0; i < indices.length; i += 3) {
997
+ const vA = indices[i];
998
+ const vB = indices[i + 1];
999
+ const vC = indices[i + 2];
1000
+ const cb = face(vA, vB, vC);
1001
+ for (const v of [vA, vB, vC]) {
1002
+ const n = get(normals, v);
1003
+ set(v, n[0] + cb[0], n[1] + cb[1], n[2] + cb[2]);
1004
+ }
1005
+ }
1006
+ } else {
1007
+ for (let i = 0; i < positions.length / 3; i += 3) {
1008
+ const cb = face(i, i + 1, i + 2);
1009
+ set(i, cb[0], cb[1], cb[2]);
1010
+ set(i + 1, cb[0], cb[1], cb[2]);
1011
+ set(i + 2, cb[0], cb[1], cb[2]);
1012
+ }
1013
+ }
1014
+ for (let i = 0; i < normals.length / 3; i++) {
1015
+ const [x, y, z] = get(normals, i);
1016
+ const s = 1 / (Math.sqrt(x * x + y * y + z * z) || 1);
1017
+ set(i, x * s, y * s, z * s);
1018
+ }
1019
+ return normals;
1020
+ }
1021
+ function extrude(outline, depth = 1, steps = 1, bevel = void 0) {
1022
+ const parameters = { shape: outline, depth, steps, bevel: bevel ?? {} };
1023
+ const verticesArray = [];
1024
+ const uvArray = [];
1025
+ const groups = [];
1026
+ const placeholder = [];
1027
+ const bevelEnabled = bevel !== false;
1028
+ const spec = bevel === false || bevel === void 0 ? {} : bevel;
1029
+ let bevelThickness = spec.thickness !== void 0 ? spec.thickness : 0.2;
1030
+ let bevelSize = spec.size !== void 0 ? spec.size : bevelThickness - 0.1;
1031
+ let bevelOffset = spec.offset !== void 0 ? spec.offset : 0;
1032
+ let bevelSegments = spec.segments !== void 0 ? spec.segments : 3;
1033
+ if (!bevelEnabled) {
1034
+ bevelSegments = 0;
1035
+ bevelThickness = 0;
1036
+ bevelSize = 0;
1037
+ bevelOffset = 0;
1038
+ }
1039
+ let vertices = pathPoints(outline.outer);
1040
+ const holes = (outline.holes ?? []).map(pathPoints);
1041
+ const reverse = !isClockWise(vertices);
1042
+ if (reverse) {
1043
+ vertices = vertices.reverse();
1044
+ for (let h = 0; h < holes.length; h++) {
1045
+ const ahole = holes[h];
1046
+ if (isClockWise(ahole)) holes[h] = ahole.reverse();
1047
+ }
1048
+ }
1049
+ const faces = triangulateShape(vertices, holes);
1050
+ const contour = vertices;
1051
+ for (const ahole of holes) vertices = vertices.concat(ahole);
1052
+ const scalePt2 = (pt, vec, size) => ({ x: pt.x + vec.x * size, y: pt.y + vec.y * size });
1053
+ const vlen = vertices.length;
1054
+ const flen = faces.length;
1055
+ const getBevelVec = (inPt, inPrev, inNext) => {
1056
+ let vTransX;
1057
+ let vTransY;
1058
+ let shrinkBy;
1059
+ const vPrevX = inPt.x - inPrev.x;
1060
+ const vPrevY = inPt.y - inPrev.y;
1061
+ const vNextX = inNext.x - inPt.x;
1062
+ const vNextY = inNext.y - inPt.y;
1063
+ const vPrevLensq = vPrevX * vPrevX + vPrevY * vPrevY;
1064
+ const collinear0 = vPrevX * vNextY - vPrevY * vNextX;
1065
+ if (Math.abs(collinear0) > Number.EPSILON) {
1066
+ const vPrevLen = Math.sqrt(vPrevLensq);
1067
+ const vNextLen = Math.sqrt(vNextX * vNextX + vNextY * vNextY);
1068
+ const ptPrevShiftX = inPrev.x - vPrevY / vPrevLen;
1069
+ const ptPrevShiftY = inPrev.y + vPrevX / vPrevLen;
1070
+ const ptNextShiftX = inNext.x - vNextY / vNextLen;
1071
+ const ptNextShiftY = inNext.y + vNextX / vNextLen;
1072
+ const sf = ((ptNextShiftX - ptPrevShiftX) * vNextY - (ptNextShiftY - ptPrevShiftY) * vNextX) / (vPrevX * vNextY - vPrevY * vNextX);
1073
+ vTransX = ptPrevShiftX + vPrevX * sf - inPt.x;
1074
+ vTransY = ptPrevShiftY + vPrevY * sf - inPt.y;
1075
+ const vTransLensq = vTransX * vTransX + vTransY * vTransY;
1076
+ if (vTransLensq <= 2) return { x: vTransX, y: vTransY };
1077
+ shrinkBy = Math.sqrt(vTransLensq / 2);
1078
+ } else {
1079
+ let directionEq = false;
1080
+ if (vPrevX > Number.EPSILON) {
1081
+ if (vNextX > Number.EPSILON) directionEq = true;
1082
+ } else if (vPrevX < -Number.EPSILON) {
1083
+ if (vNextX < -Number.EPSILON) directionEq = true;
1084
+ } else if (Math.sign(vPrevY) === Math.sign(vNextY)) {
1085
+ directionEq = true;
1086
+ }
1087
+ if (directionEq) {
1088
+ vTransX = -vPrevY;
1089
+ vTransY = vPrevX;
1090
+ shrinkBy = Math.sqrt(vPrevLensq);
1091
+ } else {
1092
+ vTransX = vPrevX;
1093
+ vTransY = vPrevY;
1094
+ shrinkBy = Math.sqrt(vPrevLensq / 2);
1095
+ }
1096
+ }
1097
+ return { x: vTransX / shrinkBy, y: vTransY / shrinkBy };
1098
+ };
1099
+ const movements = (ring2) => {
1100
+ const out = [];
1101
+ for (let i = 0, il = ring2.length, j = il - 1, k = i + 1; i < il; i++, j++, k++) {
1102
+ if (j === il) j = 0;
1103
+ if (k === il) k = 0;
1104
+ out[i] = getBevelVec(ring2[i], ring2[j], ring2[k]);
1105
+ }
1106
+ return out;
1107
+ };
1108
+ const contourMovements = movements(contour);
1109
+ const holesMovements = [];
1110
+ let verticesMovements = contourMovements.concat();
1111
+ for (const ahole of holes) {
1112
+ const oneHoleMovements = movements(ahole);
1113
+ holesMovements.push(oneHoleMovements);
1114
+ verticesMovements = verticesMovements.concat(oneHoleMovements);
1115
+ }
1116
+ const v = (x, y, z) => {
1117
+ placeholder.push(x);
1118
+ placeholder.push(y);
1119
+ placeholder.push(z);
1120
+ };
1121
+ const bevelLayer = (b, zOf) => {
1122
+ const t = b / bevelSegments;
1123
+ const z = bevelThickness * Math.cos(t * Math.PI / 2);
1124
+ const bs2 = bevelSize * Math.sin(t * Math.PI / 2) + bevelOffset;
1125
+ for (let i = 0; i < contour.length; i++) {
1126
+ const vert = scalePt2(contour[i], contourMovements[i], bs2);
1127
+ v(vert.x, vert.y, zOf(z));
1128
+ }
1129
+ for (let h = 0; h < holes.length; h++) {
1130
+ const ahole = holes[h];
1131
+ const oneHoleMovements = holesMovements[h];
1132
+ for (let i = 0; i < ahole.length; i++) {
1133
+ const vert = scalePt2(ahole[i], oneHoleMovements[i], bs2);
1134
+ v(vert.x, vert.y, zOf(z));
1135
+ }
1136
+ }
1137
+ };
1138
+ for (let b = 0; b < bevelSegments; b++) bevelLayer(b, (z) => -z);
1139
+ const bs = bevelSize + bevelOffset;
1140
+ for (let i = 0; i < vlen; i++) {
1141
+ const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i];
1142
+ v(vert.x, vert.y, 0);
1143
+ }
1144
+ for (let s = 1; s <= steps; s++) {
1145
+ for (let i = 0; i < vlen; i++) {
1146
+ const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i];
1147
+ v(vert.x, vert.y, depth / steps * s);
1148
+ }
1149
+ }
1150
+ for (let b = bevelSegments - 1; b >= 0; b--) bevelLayer(b, (z) => depth + z);
1151
+ const addVertex = (index) => {
1152
+ verticesArray.push(placeholder[index * 3 + 0]);
1153
+ verticesArray.push(placeholder[index * 3 + 1]);
1154
+ verticesArray.push(placeholder[index * 3 + 2]);
1155
+ };
1156
+ const addUV = (x, y) => {
1157
+ uvArray.push(x);
1158
+ uvArray.push(y);
1159
+ };
1160
+ const at = (index, axis) => verticesArray[index * 3 + axis];
1161
+ const f3 = (a, b, c) => {
1162
+ addVertex(a);
1163
+ addVertex(b);
1164
+ addVertex(c);
1165
+ const nextIndex = verticesArray.length / 3;
1166
+ for (const index of [nextIndex - 3, nextIndex - 2, nextIndex - 1]) addUV(at(index, 0), at(index, 1));
1167
+ };
1168
+ const f4 = (a, b, c, d) => {
1169
+ addVertex(a);
1170
+ addVertex(b);
1171
+ addVertex(d);
1172
+ addVertex(b);
1173
+ addVertex(c);
1174
+ addVertex(d);
1175
+ const nextIndex = verticesArray.length / 3;
1176
+ const iA = nextIndex - 6;
1177
+ const iB = nextIndex - 3;
1178
+ const iC = nextIndex - 2;
1179
+ const iD = nextIndex - 1;
1180
+ const byX = Math.abs(at(iA, 1) - at(iB, 1)) < Math.abs(at(iA, 0) - at(iB, 0));
1181
+ const uv = (index) => [byX ? at(index, 0) : at(index, 1), 1 - at(index, 2)];
1182
+ const uvs = [uv(iA), uv(iB), uv(iC), uv(iD)];
1183
+ for (const k of [0, 1, 3, 1, 2, 3]) addUV(...uvs[k]);
1184
+ };
1185
+ let start = verticesArray.length / 3;
1186
+ if (bevelEnabled) {
1187
+ let offset = 0;
1188
+ for (let i = 0; i < flen; i++) {
1189
+ const face = faces[i];
1190
+ f3(face[2] + offset, face[1] + offset, face[0] + offset);
1191
+ }
1192
+ const layer = steps + bevelSegments * 2;
1193
+ offset = vlen * layer;
1194
+ for (let i = 0; i < flen; i++) {
1195
+ const face = faces[i];
1196
+ f3(face[0] + offset, face[1] + offset, face[2] + offset);
1197
+ }
1198
+ } else {
1199
+ for (let i = 0; i < flen; i++) {
1200
+ const face = faces[i];
1201
+ f3(face[2], face[1], face[0]);
1202
+ }
1203
+ for (let i = 0; i < flen; i++) {
1204
+ const face = faces[i];
1205
+ f3(face[0] + vlen * steps, face[1] + vlen * steps, face[2] + vlen * steps);
1206
+ }
1207
+ }
1208
+ groups.push({ start, count: verticesArray.length / 3 - start, materialIndex: 0 });
1209
+ start = verticesArray.length / 3;
1210
+ const sidewalls = (ring2, layeroffset2) => {
1211
+ let i = ring2.length;
1212
+ while (--i >= 0) {
1213
+ const j = i;
1214
+ let k = i - 1;
1215
+ if (k < 0) k = ring2.length - 1;
1216
+ for (let s = 0, sl = steps + bevelSegments * 2; s < sl; s++) {
1217
+ const slen1 = vlen * s;
1218
+ const slen2 = vlen * (s + 1);
1219
+ f4(layeroffset2 + j + slen1, layeroffset2 + k + slen1, layeroffset2 + k + slen2, layeroffset2 + j + slen2);
1220
+ }
1221
+ }
1222
+ };
1223
+ let layeroffset = 0;
1224
+ sidewalls(contour, layeroffset);
1225
+ layeroffset += contour.length;
1226
+ for (const ahole of holes) {
1227
+ sidewalls(ahole, layeroffset);
1228
+ layeroffset += ahole.length;
1229
+ }
1230
+ groups.push({ start, count: verticesArray.length / 3 - start, materialIndex: 1 });
1231
+ const positions = new Float32Array(verticesArray);
1232
+ return { type: "ExtrudeGeometry", parameters, positions, normals: computeVertexNormals(positions), uvs: new Float32Array(uvArray), groups };
1233
+ }
1234
+ var PARAMETER_KEYS = {
1235
+ box: ["size", "segments"],
1236
+ cylinder: ["radiusTop", "radiusBottom", "height", "radialSegments", "heightSegments", "openEnded", "thetaStart", "thetaLength"],
1237
+ cone: ["radius", "height", "radialSegments", "heightSegments", "openEnded", "thetaStart", "thetaLength"],
1238
+ sphere: ["radius", "widthSegments", "heightSegments", "phiStart", "phiLength", "thetaStart", "thetaLength"],
1239
+ plane: ["width", "height", "widthSegments", "heightSegments"],
1240
+ circle: ["radius", "segments", "thetaStart", "thetaLength"],
1241
+ ring: ["innerRadius", "outerRadius", "thetaSegments", "phiSegments", "thetaStart", "thetaLength"],
1242
+ lathe: ["points", "segments", "phiStart", "phiLength"],
1243
+ torus: ["radius", "tube", "radialSegments", "tubularSegments", "arc"],
1244
+ extrude: ["shape", "depth", "steps", "bevel"],
1245
+ difference: ["size", "subtract"],
1246
+ mesh: ["positions", "normals", "uvs", "indices"]
1247
+ };
1248
+ var SEGMENT_KEYS = {
1249
+ cylinder: ["radialSegments"],
1250
+ cone: ["radialSegments"],
1251
+ sphere: ["widthSegments", "heightSegments"],
1252
+ circle: ["segments"],
1253
+ ring: ["thetaSegments"],
1254
+ lathe: ["segments"],
1255
+ torus: ["radialSegments", "tubularSegments"]
1256
+ };
1257
+ var SEGMENT_DEFAULTS = { radialSegments: 32, widthSegments: 32, heightSegments: 16, segments: 12, thetaSegments: 32, tubularSegments: 48 };
1258
+ var segmentDefault = (kind, key) => kind === "circle" && key === "segments" ? 32 : kind === "torus" && key === "radialSegments" ? 12 : SEGMENT_DEFAULTS[key];
1259
+ function primitiveParameters(node, options = {}) {
1260
+ const out = {};
1261
+ const source = node;
1262
+ for (const key of PARAMETER_KEYS[node.kind]) if (source[key] !== void 0) out[key] = source[key];
1263
+ const detail = options.detail ?? 1;
1264
+ if (detail !== 1) {
1265
+ const floor = options.minSegments ?? 3;
1266
+ for (const key of SEGMENT_KEYS[node.kind] ?? []) {
1267
+ const given = out[key] ?? segmentDefault(node.kind, key);
1268
+ out[key] = Math.max(floor, Math.round(given * detail));
1269
+ }
1270
+ }
1271
+ return out;
1272
+ }
1273
+ function mesh(p) {
1274
+ const positions = new Float32Array(p.positions);
1275
+ const indices = p.indices ? indexArray(p.indices) : void 0;
1276
+ return {
1277
+ type: "BufferGeometry",
1278
+ parameters: {},
1279
+ positions,
1280
+ normals: p.normals ? new Float32Array(p.normals) : computeVertexNormals(positions, indices),
1281
+ uvs: p.uvs ? new Float32Array(p.uvs) : new Float32Array(positions.length / 3 * 2),
1282
+ ...indices ? { indices } : {},
1283
+ groups: []
1284
+ };
1285
+ }
1286
+ function make(kind, p) {
1287
+ switch (kind) {
1288
+ case "box": {
1289
+ const size = p.size ?? [1, 1, 1];
1290
+ const segments = p.segments ?? [1, 1, 1];
1291
+ return box(size[0], size[1], size[2], segments[0], segments[1], segments[2]);
1292
+ }
1293
+ case "cylinder":
1294
+ return cylinder(p.radiusTop, p.radiusBottom, p.height, p.radialSegments, p.heightSegments, p.openEnded, p.thetaStart, p.thetaLength);
1295
+ case "cone":
1296
+ return cone(p.radius, p.height, p.radialSegments, p.heightSegments, p.openEnded, p.thetaStart, p.thetaLength);
1297
+ case "sphere":
1298
+ return sphere(p.radius, p.widthSegments, p.heightSegments, p.phiStart, p.phiLength, p.thetaStart, p.thetaLength);
1299
+ case "plane":
1300
+ return plane(p.width, p.height, p.widthSegments, p.heightSegments);
1301
+ case "circle":
1302
+ return circle(p.radius, p.segments, p.thetaStart, p.thetaLength);
1303
+ case "ring":
1304
+ return ring(p.innerRadius, p.outerRadius, p.thetaSegments, p.phiSegments, p.thetaStart, p.thetaLength);
1305
+ case "lathe":
1306
+ return lathe(p.points, p.segments, p.phiStart, p.phiLength);
1307
+ case "torus":
1308
+ return torus(p.radius, p.tube, p.radialSegments, p.tubularSegments, p.arc);
1309
+ case "extrude":
1310
+ return extrude(p.shape, p.depth, p.steps, p.bevel);
1311
+ case "difference":
1312
+ return difference(p.size, p.subtract ?? []);
1313
+ case "mesh":
1314
+ return mesh(p);
1315
+ default:
1316
+ throw new Error(`game-shapes: unknown primitive kind "${String(kind)}"`);
1317
+ }
1318
+ }
1319
+ var templates = /* @__PURE__ */ new Map();
1320
+ var templateLimit = 4096;
1321
+ var stats = { hits: 0, misses: 0 };
1322
+ function buildPrimitive(node, options = {}) {
1323
+ const parameters = primitiveParameters(node, options);
1324
+ if (node.kind === "mesh" || templateLimit === 0) return make(node.kind, parameters);
1325
+ const key = `${node.kind}:${canonicalJson(parameters)}`;
1326
+ const cached = templates.get(key);
1327
+ if (cached) {
1328
+ stats.hits++;
1329
+ templates.delete(key);
1330
+ templates.set(key, cached);
1331
+ return cached;
1332
+ }
1333
+ stats.misses++;
1334
+ const built = make(node.kind, parameters);
1335
+ templates.set(key, built);
1336
+ if (templates.size > templateLimit) templates.delete(templates.keys().next().value);
1337
+ return built;
1338
+ }
1339
+ var num = (v) => typeof v === "number" && Number.isFinite(v) ? null : "expected a finite number";
1340
+ var bool = (v) => typeof v === "boolean" ? null : "expected a boolean";
1341
+ var str = (v) => typeof v === "string" ? null : "expected a string";
1342
+ var int = (min) => (v) => typeof v === "number" && Number.isInteger(v) && v >= min ? null : `expected an integer >= ${min}`;
1343
+ 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}]`;
1344
+ var tuple = (n, item = num) => (v) => {
1345
+ if (!Array.isArray(v) || v.length !== n) return `expected an array of ${n}`;
1346
+ for (const x of v) if (item(x)) return `expected an array of ${n} numbers`;
1347
+ return null;
1348
+ };
1349
+ var list = (item, min = 0) => (v) => {
1350
+ if (!Array.isArray(v)) return "expected an array";
1351
+ if (v.length < min) return `expected at least ${min} items`;
1352
+ for (let i = 0; i < v.length; i++) {
1353
+ const e = item(v[i]);
1354
+ if (e) return `[${i}]: ${e}`;
1355
+ }
1356
+ return null;
1357
+ };
1358
+ 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"';
1359
+ var oneOf = (values) => (v) => values.includes(v) ? null : `expected one of ${values.map((x) => JSON.stringify(x)).join(", ")}`;
1360
+ var object = (fields, required = []) => (v) => {
1361
+ if (!v || typeof v !== "object" || Array.isArray(v)) return "expected an object";
1362
+ const record = v;
1363
+ for (const key of required) if (record[key] === void 0) return `${key}: required`;
1364
+ for (const key of Object.keys(record)) {
1365
+ if (record[key] === void 0) continue;
1366
+ const check = fields[key];
1367
+ if (!check) return `${key}: unknown field`;
1368
+ const e = check(record[key]);
1369
+ if (e) return `${key}: ${e}`;
1370
+ }
1371
+ return null;
1372
+ };
1373
+ var ORDERS = ["XYZ", "YXZ", "ZXY", "ZYX", "YZX", "XZY"];
1374
+ var TRANSFORM = {
1375
+ position: tuple(3),
1376
+ rotation: tuple(3),
1377
+ rotationOrder: oneOf(ORDERS),
1378
+ scale: (v) => typeof v === "number" ? num(v) : tuple(3)(v),
1379
+ heading: num,
1380
+ matrix: tuple(16)
1381
+ };
1382
+ var UV = object({ u0: num, v0: num, u1: num, v1: num }, ["u0", "v0", "u1", "v1"]);
1383
+ var INSTANCE = object({ ...TRANSFORM, color });
1384
+ var COMMON = {
1385
+ ...TRANSFORM,
1386
+ kind: str,
1387
+ name: str,
1388
+ part: str,
1389
+ material: str,
1390
+ color,
1391
+ uv: UV,
1392
+ instances: list(INSTANCE),
1393
+ minDetail: range(0, 1, true),
1394
+ tags: list(str)
1395
+ };
1396
+ var vec2 = tuple(2);
1397
+ var BEVEL = (v) => v === false ? null : object({ thickness: num, size: num, offset: num, segments: int(1) })(v);
1398
+ var KINDS = {
1399
+ group: { fields: {} },
1400
+ box: { fields: { size: tuple(3), segments: tuple(3, int(1)) } },
1401
+ cylinder: { fields: { radiusTop: num, radiusBottom: num, height: num, radialSegments: int(1), heightSegments: int(1), openEnded: bool, thetaStart: num, thetaLength: num } },
1402
+ cone: { fields: { radius: num, height: num, radialSegments: int(1), heightSegments: int(1), openEnded: bool, thetaStart: num, thetaLength: num } },
1403
+ sphere: { fields: { radius: num, widthSegments: int(3), heightSegments: int(2), phiStart: num, phiLength: num, thetaStart: num, thetaLength: num } },
1404
+ plane: { fields: { width: num, height: num, widthSegments: int(1), heightSegments: int(1) } },
1405
+ circle: { fields: { radius: num, segments: int(3), thetaStart: num, thetaLength: num } },
1406
+ ring: { fields: { innerRadius: num, outerRadius: num, thetaSegments: int(3), phiSegments: int(1), thetaStart: num, thetaLength: num } },
1407
+ lathe: { fields: { points: list(vec2, 2), segments: int(1), phiStart: num, phiLength: num }, required: ["points"] },
1408
+ torus: { fields: { radius: num, tube: num, radialSegments: int(1), tubularSegments: int(1), arc: num } },
1409
+ extrude: {
1410
+ fields: { shape: object({ outer: list(vec2, 3), holes: list(list(vec2, 3)) }, ["outer"]), depth: num, steps: int(1), bevel: BEVEL },
1411
+ required: ["shape"]
1412
+ },
1413
+ difference: { fields: { size: tuple(3), subtract: list(object({ min: vec2, max: vec2 }, ["min", "max"])) }, required: ["size", "subtract"] },
1414
+ mesh: {
1415
+ fields: {
1416
+ positions: (v) => list(num)(v) ?? (v.length % 3 === 0 ? null : "expected a multiple of 3 numbers"),
1417
+ normals: list(num),
1418
+ uvs: list(num),
1419
+ indices: list(int(0))
1420
+ },
1421
+ required: ["positions"]
1422
+ },
1423
+ point: { fields: { data: () => null } },
1424
+ custom: { fields: { generator: str, params: () => null }, required: ["generator"] }
1425
+ };
1426
+
1427
+ // ../game-shapes-three/dist/index.js
1428
+ var UV_BEFORE_NORMAL = /* @__PURE__ */ new Set(["LatheGeometry", "ExtrudeGeometry"]);
1429
+ var INDEX_FIRST = /* @__PURE__ */ new Set(["BoxGeometry", "CylinderGeometry", "ConeGeometry", "SphereGeometry", "PlaneGeometry", "CircleGeometry", "RingGeometry", "LatheGeometry", "TorusGeometry"]);
1430
+ var isPrimitive = (value) => typeof value.type === "string";
1431
+ function threeParameters(THREE, p) {
1432
+ if (p.type === "LatheGeometry") {
1433
+ const points = p.parameters.points.map(({ x, y }) => new THREE.Vector2(x, y));
1434
+ return { ...p.parameters, points };
1435
+ }
1436
+ return { ...p.parameters };
1437
+ }
1438
+ function toGeometry(THREE, source, options = {}) {
1439
+ const copy = options.copy !== false;
1440
+ const primitive = isPrimitive(source) ? source : null;
1441
+ const ThreeClass = primitive && primitive.type !== "BufferGeometry" ? THREE[primitive.type] : void 0;
1442
+ const geometry = ThreeClass ? Reflect.construct(THREE.BufferGeometry, [], ThreeClass) : new THREE.BufferGeometry();
1443
+ if (primitive && ThreeClass) {
1444
+ Object.assign(geometry, { type: primitive.type, parameters: threeParameters(THREE, primitive) });
1445
+ }
1446
+ const attribute = (array, size) => copy ? new THREE.Float32BufferAttribute(array, size) : new THREE.BufferAttribute(array, size);
1447
+ const index = source.indices;
1448
+ const setIndex = () => {
1449
+ if (!index) return;
1450
+ geometry.setIndex(copy ? index instanceof Uint32Array ? new THREE.Uint32BufferAttribute(index, 1) : new THREE.Uint16BufferAttribute(index, 1) : new THREE.BufferAttribute(index, 1));
1451
+ };
1452
+ const type = primitive?.type ?? "BufferGeometry";
1453
+ if (INDEX_FIRST.has(type)) setIndex();
1454
+ geometry.setAttribute("position", attribute(source.positions, 3));
1455
+ if (UV_BEFORE_NORMAL.has(type)) {
1456
+ geometry.setAttribute("uv", attribute(source.uvs, 2));
1457
+ geometry.setAttribute("normal", attribute(source.normals, 3));
1458
+ } else {
1459
+ geometry.setAttribute("normal", attribute(source.normals, 3));
1460
+ geometry.setAttribute("uv", attribute(source.uvs, 2));
1461
+ }
1462
+ if (!primitive && source.colors) geometry.setAttribute("color", attribute(source.colors, 3));
1463
+ if (!INDEX_FIRST.has(type)) setIndex();
1464
+ if (primitive) for (const group of primitive.groups) geometry.addGroup(group.start, group.count, group.materialIndex);
1465
+ return geometry;
1466
+ }
1467
+ var SHAPE_GEOMETRY_TYPES = ["BoxGeometry", "CylinderGeometry", "ConeGeometry", "SphereGeometry", "PlaneGeometry", "CircleGeometry", "RingGeometry", "LatheGeometry", "TorusGeometry", "ExtrudeGeometry"];
1468
+ function linePoints(path) {
1469
+ if (path.autoClose || path.curves.length === 0) return null;
1470
+ const points = [];
1471
+ for (const [index, curve] of path.curves.entries()) {
1472
+ if (!curve.isLineCurve || !curve.v1 || !curve.v2) return null;
1473
+ if (index === 0) points.push([curve.v1.x, curve.v1.y]);
1474
+ else {
1475
+ const last = points[points.length - 1];
1476
+ if (last[0] !== curve.v1.x || last[1] !== curve.v1.y) return null;
1477
+ }
1478
+ points.push([curve.v2.x, curve.v2.y]);
1479
+ }
1480
+ return points;
1481
+ }
1482
+ function extrudeOf(shapes, options = {}) {
1483
+ if (Array.isArray(shapes) || !shapes || options.extrudePath !== void 0 || options.UVGenerator !== void 0) return null;
1484
+ const shape = shapes;
1485
+ const outer = linePoints(shape);
1486
+ if (!outer) return null;
1487
+ const holes = [];
1488
+ for (const hole of shape.holes ?? []) {
1489
+ const points = linePoints(hole);
1490
+ if (!points) return null;
1491
+ holes.push(points);
1492
+ }
1493
+ const outline = holes.length ? { outer, holes } : { outer };
1494
+ const bevel = options.bevelEnabled === false ? false : {
1495
+ ...options.bevelThickness !== void 0 ? { thickness: options.bevelThickness } : {},
1496
+ ...options.bevelSize !== void 0 ? { size: options.bevelSize } : {},
1497
+ ...options.bevelOffset !== void 0 ? { offset: options.bevelOffset } : {},
1498
+ ...options.bevelSegments !== void 0 ? { segments: options.bevelSegments } : {}
1499
+ };
1500
+ return extrude(outline, options.depth, options.steps, bevel);
1501
+ }
1502
+ var namespaces = /* @__PURE__ */ new WeakMap();
1503
+ var shims = /* @__PURE__ */ new WeakSet();
1504
+ function shapeGeometries(THREE, options = {}) {
1505
+ if (shims.has(THREE)) return THREE;
1506
+ const cache2 = options.cache !== false;
1507
+ const key = cache2 ? "cache" : "nocache";
1508
+ let byKey = namespaces.get(THREE);
1509
+ if (!byKey) {
1510
+ byKey = /* @__PURE__ */ new Map();
1511
+ namespaces.set(THREE, byKey);
1512
+ }
1513
+ const existing = byKey.get(key);
1514
+ if (existing) return existing;
1515
+ const made = (p) => toGeometry(THREE, p);
1516
+ const viaCache = (node, direct) => cache2 ? buildPrimitive(node) : direct();
1517
+ const defined = (o) => Object.fromEntries(Object.entries(o).filter(([, v]) => v !== void 0));
1518
+ const constructors = {
1519
+ BoxGeometry: (width, height, depth, ws = 1, hs = 1, ds = 1) => made(viaCache({ kind: "box", size: [width ?? 1, height ?? 1, depth ?? 1], segments: [ws, hs, ds] }, () => box(width, height, depth, ws, hs, ds))),
1520
+ CylinderGeometry: (radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength) => made(viaCache({ kind: "cylinder", ...defined({ radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength }) }, () => cylinder(radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength))),
1521
+ ConeGeometry: (radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength) => made(viaCache({ kind: "cone", ...defined({ radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength }) }, () => cone(radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength))),
1522
+ SphereGeometry: (radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength) => made(viaCache({ kind: "sphere", ...defined({ radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength }) }, () => sphere(radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength))),
1523
+ PlaneGeometry: (width, height, widthSegments, heightSegments) => made(viaCache({ kind: "plane", ...defined({ width, height, widthSegments, heightSegments }) }, () => plane(width, height, widthSegments, heightSegments))),
1524
+ CircleGeometry: (radius, segments, thetaStart, thetaLength) => made(viaCache({ kind: "circle", ...defined({ radius, segments, thetaStart, thetaLength }) }, () => circle(radius, segments, thetaStart, thetaLength))),
1525
+ RingGeometry: (innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength) => made(viaCache({ kind: "ring", ...defined({ innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength }) }, () => ring(innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength))),
1526
+ LatheGeometry: (points, segments, phiStart, phiLength) => {
1527
+ if (!points) return new THREE.LatheGeometry(void 0, segments, phiStart, phiLength);
1528
+ const profile = points.map((p) => [p.x, p.y]);
1529
+ return made(viaCache({ kind: "lathe", points: profile, ...defined({ segments, phiStart, phiLength }) }, () => lathe(profile, segments, phiStart, phiLength)));
1530
+ },
1531
+ TorusGeometry: (radius, tube2, radialSegments, tubularSegments, arc) => made(viaCache({ kind: "torus", ...defined({ radius, tube: tube2, radialSegments, tubularSegments, arc }) }, () => torus(radius, tube2, radialSegments, tubularSegments, arc))),
1532
+ ExtrudeGeometry: (shapes, extrudeOptions) => {
1533
+ const buffers = shapes === void 0 ? null : extrudeOf(shapes, extrudeOptions);
1534
+ if (!buffers) return new THREE.ExtrudeGeometry(shapes, extrudeOptions);
1535
+ const geometry = toGeometry(THREE, buffers);
1536
+ geometry.parameters = { shapes, options: extrudeOptions ?? {} };
1537
+ return geometry;
1538
+ }
1539
+ };
1540
+ const namespace = { ...THREE };
1541
+ for (const type of SHAPE_GEOMETRY_TYPES) {
1542
+ const make2 = constructors[type];
1543
+ const Constructor = function(...args) {
1544
+ return make2(...args);
1545
+ };
1546
+ Constructor.prototype = THREE[type].prototype;
1547
+ Object.defineProperty(Constructor, "name", { value: type });
1548
+ namespace[type] = Constructor;
1549
+ }
1550
+ const result = namespace;
1551
+ byKey.set(key, result);
1552
+ shims.add(result);
1553
+ return result;
1554
+ }
1555
+
39
1556
  // src/rounded-box.ts
40
1557
  function createRoundedBox(THREE, width, height, depth, radius) {
41
- if (radius === 0) return new THREE.BoxGeometry(width, height, depth);
42
- const geometry = new THREE.BoxGeometry(2, 2, 2, 4, 4, 4);
1558
+ const T = shapeGeometries(THREE);
1559
+ if (radius === 0) return new T.BoxGeometry(width, height, depth);
1560
+ const geometry = new T.BoxGeometry(2, 2, 2, 4, 4, 4);
43
1561
  const position = geometry.attributes.position;
44
1562
  const normal = geometry.attributes.normal;
45
1563
  const half = [width / 2, height / 2, depth / 2];
@@ -76,7 +1594,7 @@ var GameUrbanThree = (() => {
76
1594
  shape.lineTo(width / 2, 0);
77
1595
  shape.lineTo(0, height);
78
1596
  shape.lineTo(-width / 2, 0);
79
- const geometry = new THREE.ExtrudeGeometry(shape, { depth, bevelEnabled: false });
1597
+ const geometry = new (shapeGeometries(THREE)).ExtrudeGeometry(shape, { depth, bevelEnabled: false });
80
1598
  geometry.translate(0, 0, -depth / 2);
81
1599
  return geometry;
82
1600
  }
@@ -95,11 +1613,11 @@ var GameUrbanThree = (() => {
95
1613
  g.textAlign = "center";
96
1614
  g.textBaseline = "middle";
97
1615
  const base = g.font;
98
- lines.forEach((text, index) => {
1616
+ lines.forEach((text2, index) => {
99
1617
  g.font = base;
100
- const measured = g.measureText(text).width;
1618
+ const measured = g.measureText(text2).width;
101
1619
  if (measured > o.width * FIT) g.font = base.replace(/(\d+(?:\.\d+)?)px/, (_match, size) => (Number(size) * o.width * FIT / measured).toFixed(1) + "px");
102
- g.fillText(text, o.width / 2, o.height / 2 + (index - (lines.length - 1) / 2) * (o.height / lines.length));
1620
+ g.fillText(text2, o.width / 2, o.height / 2 + (index - (lines.length - 1) / 2) * (o.height / lines.length));
103
1621
  });
104
1622
  const texture = new THREE.CanvasTexture(canvas);
105
1623
  texture.anisotropy = 4;
@@ -115,25 +1633,26 @@ var GameUrbanThree = (() => {
115
1633
  var SIGN_FONT = '700 56px "Arial Narrow", Arial, sans-serif';
116
1634
  var SIGN_CANVAS = { width: 512, height: 128 };
117
1635
  function createUrbanThree(THREE, options = {}) {
1636
+ const T = shapeGeometries(THREE);
118
1637
  const geometries = /* @__PURE__ */ new Map();
119
1638
  const materials = /* @__PURE__ */ new Map();
120
1639
  const disposables = [];
121
- const standard = (color, style, flat = false) => {
1640
+ const standard = (color2, style, flat = false) => {
122
1641
  const roughness = style?.roughness ?? DEFAULT_ROUGHNESS;
123
1642
  const metalness = style?.metalness ?? DEFAULT_METALNESS;
124
- const key = `s|${color}|${roughness}|${metalness}|${flat ? 1 : 0}`;
1643
+ const key = `s|${color2}|${roughness}|${metalness}|${flat ? 1 : 0}`;
125
1644
  let material = materials.get(key);
126
1645
  if (!material) {
127
- material = new THREE.MeshStandardMaterial({ color, roughness, metalness, flatShading: flat });
1646
+ material = new THREE.MeshStandardMaterial({ color: color2, roughness, metalness, flatShading: flat });
128
1647
  materials.set(key, material);
129
1648
  }
130
1649
  return material;
131
1650
  };
132
- const glowing = (color) => {
133
- const key = `b|${color}`;
1651
+ const glowing = (color2) => {
1652
+ const key = `b|${color2}`;
134
1653
  let material = materials.get(key);
135
1654
  if (!material) {
136
- material = new THREE.MeshBasicMaterial({ color });
1655
+ material = new THREE.MeshBasicMaterial({ color: color2 });
137
1656
  materials.set(key, material);
138
1657
  }
139
1658
  return material;
@@ -147,9 +1666,9 @@ var GameUrbanThree = (() => {
147
1666
  }
148
1667
  return geometry;
149
1668
  };
150
- const shadows = (mesh, style, cast, receive) => {
151
- mesh.castShadow = style?.castShadow ?? cast;
152
- mesh.receiveShadow = style?.receiveShadow ?? receive;
1669
+ const shadows = (mesh2, style, cast, receive) => {
1670
+ mesh2.castShadow = style?.castShadow ?? cast;
1671
+ mesh2.receiveShadow = style?.receiveShadow ?? receive;
153
1672
  };
154
1673
  const texture = (lines, width, height, background, foreground, font) => {
155
1674
  if (!options.document) throw new Error("createUrbanThree needs a document (option) to draw signs and labels");
@@ -158,12 +1677,12 @@ var GameUrbanThree = (() => {
158
1677
  return map;
159
1678
  };
160
1679
  const textPlane = (part, map) => {
161
- const mesh = new THREE.Mesh(new THREE.PlaneGeometry(part.width, part.height), new THREE.MeshBasicMaterial({ map, transparent: true }));
162
- mesh.userData.own = true;
163
- mesh.position.set(part.x, part.y, part.z);
164
- if (part.ry) mesh.rotation.y = part.ry;
165
- disposables.push(mesh.geometry, mesh.material);
166
- return mesh;
1680
+ const mesh2 = new THREE.Mesh(new T.PlaneGeometry(part.width, part.height), new THREE.MeshBasicMaterial({ map, transparent: true }));
1681
+ mesh2.userData.own = true;
1682
+ mesh2.position.set(part.x, part.y, part.z);
1683
+ if (part.ry) mesh2.rotation.y = part.ry;
1684
+ disposables.push(mesh2.geometry, mesh2.material);
1685
+ return mesh2;
167
1686
  };
168
1687
  const drawPart = (part, animators, lights) => {
169
1688
  switch (part.shape) {
@@ -174,94 +1693,94 @@ var GameUrbanThree = (() => {
174
1693
  return [group];
175
1694
  }
176
1695
  case "box": {
177
- const mesh = new THREE.Mesh(roundedBox(part.width, part.height, part.depth, part.radius), part.emissive ? glowing(part.color) : standard(part.color, part.style));
178
- mesh.position.set(part.x, part.y, part.z);
179
- if (part.ry) mesh.rotation.y = part.ry;
180
- if (part.rz) mesh.rotation.z = part.rz;
181
- shadows(mesh, part.style, true, true);
182
- return [mesh];
1696
+ const mesh2 = new THREE.Mesh(roundedBox(part.width, part.height, part.depth, part.radius), part.emissive ? glowing(part.color) : standard(part.color, part.style));
1697
+ mesh2.position.set(part.x, part.y, part.z);
1698
+ if (part.ry) mesh2.rotation.y = part.ry;
1699
+ if (part.rz) mesh2.rotation.z = part.rz;
1700
+ shadows(mesh2, part.style, true, true);
1701
+ return [mesh2];
183
1702
  }
184
1703
  case "prism": {
185
1704
  const geometry = createPrism(THREE, part.width, part.height, part.depth);
186
1705
  disposables.push(geometry);
187
- const mesh = new THREE.Mesh(geometry, standard(part.color, part.style, true));
188
- mesh.position.set(part.x, part.y, part.z);
189
- if (part.ry) mesh.rotation.y = part.ry;
190
- shadows(mesh, part.style, true, true);
191
- return [mesh];
1706
+ const mesh2 = new THREE.Mesh(geometry, standard(part.color, part.style, true));
1707
+ mesh2.position.set(part.x, part.y, part.z);
1708
+ if (part.ry) mesh2.rotation.y = part.ry;
1709
+ shadows(mesh2, part.style, true, true);
1710
+ return [mesh2];
192
1711
  }
193
1712
  case "cylinder": {
194
- const geometry = new THREE.CylinderGeometry(part.radiusTop, part.radiusBottom, part.height, part.segments);
1713
+ const geometry = new T.CylinderGeometry(part.radiusTop, part.radiusBottom, part.height, part.segments);
195
1714
  disposables.push(geometry);
196
- const mesh = new THREE.Mesh(geometry, standard(part.color, part.style));
197
- mesh.position.set(part.x, part.y, part.z);
198
- shadows(mesh, part.style, true, true);
199
- return [mesh];
1715
+ const mesh2 = new THREE.Mesh(geometry, standard(part.color, part.style));
1716
+ mesh2.position.set(part.x, part.y, part.z);
1717
+ shadows(mesh2, part.style, true, true);
1718
+ return [mesh2];
200
1719
  }
201
1720
  case "cone": {
202
- const geometry = new THREE.ConeGeometry(part.radius, part.height, part.segments);
1721
+ const geometry = new T.ConeGeometry(part.radius, part.height, part.segments);
203
1722
  disposables.push(geometry);
204
- const mesh = new THREE.Mesh(geometry, standard(part.color, part.style, true));
205
- mesh.position.set(part.x, part.y, part.z);
206
- shadows(mesh, part.style, true, true);
207
- return [mesh];
1723
+ const mesh2 = new THREE.Mesh(geometry, standard(part.color, part.style, true));
1724
+ mesh2.position.set(part.x, part.y, part.z);
1725
+ shadows(mesh2, part.style, true, true);
1726
+ return [mesh2];
208
1727
  }
209
1728
  case "crown": {
210
1729
  const geometry = new THREE.DodecahedronGeometry(part.radius, 0);
211
1730
  disposables.push(geometry);
212
- const mesh = new THREE.Mesh(geometry, standard(part.color, part.style));
213
- mesh.position.set(part.x, part.y, part.z);
214
- shadows(mesh, part.style, true, false);
215
- return [mesh];
1731
+ const mesh2 = new THREE.Mesh(geometry, standard(part.color, part.style));
1732
+ mesh2.position.set(part.x, part.y, part.z);
1733
+ shadows(mesh2, part.style, true, false);
1734
+ return [mesh2];
216
1735
  }
217
1736
  case "terrain": {
218
1737
  const { grid } = part;
219
- const geometry = new THREE.PlaneGeometry(grid.size, grid.size, grid.segments, grid.segments);
1738
+ const geometry = new T.PlaneGeometry(grid.size, grid.size, grid.segments, grid.segments);
220
1739
  geometry.rotateX(-Math.PI / 2);
221
1740
  const position = geometry.attributes.position;
222
1741
  const colors2 = new Float32Array(position.count * 3);
223
- const color = new THREE.Color();
1742
+ const color2 = new THREE.Color();
224
1743
  for (let index = 0; index < position.count; index++) {
225
1744
  position.setXYZ(index, grid.x[index], grid.heights[index], grid.z[index]);
226
- color.setHex(grid.colors[index]);
227
- colors2[index * 3] = color.r;
228
- colors2[index * 3 + 1] = color.g;
229
- colors2[index * 3 + 2] = color.b;
1745
+ color2.setHex(grid.colors[index]);
1746
+ colors2[index * 3] = color2.r;
1747
+ colors2[index * 3 + 1] = color2.g;
1748
+ colors2[index * 3 + 2] = color2.b;
230
1749
  }
231
1750
  geometry.setAttribute("color", new THREE.BufferAttribute(colors2, 3));
232
1751
  geometry.computeVertexNormals();
233
1752
  disposables.push(geometry);
234
1753
  const material = new THREE.MeshStandardMaterial({ vertexColors: true, flatShading: true, roughness: 1 });
235
1754
  disposables.push(material);
236
- const mesh = new THREE.Mesh(geometry, material);
237
- mesh.userData.own = true;
238
- shadows(mesh, part.style, true, true);
239
- return [mesh];
1755
+ const mesh2 = new THREE.Mesh(geometry, material);
1756
+ mesh2.userData.own = true;
1757
+ shadows(mesh2, part.style, true, true);
1758
+ return [mesh2];
240
1759
  }
241
1760
  case "vault": {
242
- const geometry = new THREE.CylinderGeometry(part.radius, part.radius, part.length, part.segments, 1, false, 0, part.arc);
1761
+ const geometry = new T.CylinderGeometry(part.radius, part.radius, part.length, part.segments, 1, false, 0, part.arc);
243
1762
  disposables.push(geometry);
244
- const mesh = new THREE.Mesh(geometry, standard(part.color, part.style));
245
- mesh.rotation.set(part.rotation.x, part.rotation.y, part.rotation.z);
246
- mesh.position.set(part.x, part.y, part.z);
247
- mesh.scale.set(part.scale.x, part.scale.y, part.scale.z);
248
- shadows(mesh, part.style, true, false);
249
- return [mesh];
1763
+ const mesh2 = new THREE.Mesh(geometry, standard(part.color, part.style));
1764
+ mesh2.rotation.set(part.rotation.x, part.rotation.y, part.rotation.z);
1765
+ mesh2.position.set(part.x, part.y, part.z);
1766
+ mesh2.scale.set(part.scale.x, part.scale.y, part.scale.z);
1767
+ shadows(mesh2, part.style, true, false);
1768
+ return [mesh2];
250
1769
  }
251
1770
  case "plane": {
252
- const geometry = new THREE.PlaneGeometry(part.width, part.depth).rotateX(-Math.PI / 2);
1771
+ const geometry = new T.PlaneGeometry(part.width, part.depth).rotateX(-Math.PI / 2);
253
1772
  disposables.push(geometry);
254
- const mesh = new THREE.Mesh(geometry, standard(part.color, part.style));
255
- mesh.position.set(part.x, part.y, part.z);
256
- shadows(mesh, part.style, false, true);
257
- return [mesh];
1773
+ const mesh2 = new THREE.Mesh(geometry, standard(part.color, part.style));
1774
+ mesh2.position.set(part.x, part.y, part.z);
1775
+ shadows(mesh2, part.style, false, true);
1776
+ return [mesh2];
258
1777
  }
259
1778
  case "line": {
260
1779
  const geometry = new THREE.BufferGeometry().setFromPoints(part.points.map(([x, y, z]) => new THREE.Vector3(x, y, z)));
261
- const mesh = new THREE.Line(geometry, new THREE.LineBasicMaterial({ color: part.color }));
262
- mesh.userData.own = true;
263
- disposables.push(geometry, mesh.material);
264
- return [mesh];
1780
+ const mesh2 = new THREE.Line(geometry, new THREE.LineBasicMaterial({ color: part.color }));
1781
+ mesh2.userData.own = true;
1782
+ disposables.push(geometry, mesh2.material);
1783
+ return [mesh2];
265
1784
  }
266
1785
  case "sign":
267
1786
  return [textPlane(part, texture([part.text], SIGN_CANVAS.width, SIGN_CANVAS.height, part.background, part.foreground, SIGN_FONT))];
@@ -272,8 +1791,8 @@ var GameUrbanThree = (() => {
272
1791
  holder.position.set(part.x, part.y, part.z);
273
1792
  if (part.ry) holder.rotation.y = part.ry;
274
1793
  if (part.scale !== 1) holder.scale.setScalar(part.scale);
275
- const object = options.resolvers?.[part.kind]?.(part);
276
- if (object) holder.add(object);
1794
+ const object2 = options.resolvers?.[part.kind]?.(part);
1795
+ if (object2) holder.add(object2);
277
1796
  holder.userData.placement = { kind: part.kind, id: part.id };
278
1797
  return [holder];
279
1798
  }
@@ -305,39 +1824,39 @@ var GameUrbanThree = (() => {
305
1824
  }
306
1825
  }
307
1826
  };
308
- const instancedItems = (list) => {
309
- const mesh = new THREE.InstancedMesh(roundedBox(1, 1, 1, ITEM_ROUNDNESS), standard(16777215), list.length);
1827
+ const instancedItems = (list2) => {
1828
+ const mesh2 = new THREE.InstancedMesh(roundedBox(1, 1, 1, ITEM_ROUNDNESS), standard(16777215), list2.length);
310
1829
  const matrix = new THREE.Matrix4();
311
1830
  const quaternion = new THREE.Quaternion();
312
1831
  const euler = new THREE.Euler();
313
1832
  const position = new THREE.Vector3();
314
1833
  const scale = new THREE.Vector3();
315
- const color = new THREE.Color();
316
- list.forEach((entry, index) => {
1834
+ const color2 = new THREE.Color();
1835
+ list2.forEach((entry, index) => {
317
1836
  euler.set(entry.rx, entry.ry, entry.rz);
318
1837
  quaternion.setFromEuler(euler);
319
1838
  matrix.compose(position.set(entry.x, entry.y + entry.height / 2, entry.z), quaternion, scale.set(entry.width, entry.height, entry.depth));
320
- mesh.setMatrixAt(index, matrix);
321
- mesh.setColorAt(index, color.set(entry.color));
1839
+ mesh2.setMatrixAt(index, matrix);
1840
+ mesh2.setColorAt(index, color2.set(entry.color));
322
1841
  });
323
- mesh.castShadow = true;
324
- mesh.receiveShadow = true;
325
- return mesh;
1842
+ mesh2.castShadow = true;
1843
+ mesh2.receiveShadow = true;
1844
+ return mesh2;
326
1845
  };
327
- const instancedWindows = (list, material) => {
328
- const geometry = new THREE.PlaneGeometry(1, 1);
1846
+ const instancedWindows = (list2, material) => {
1847
+ const geometry = new T.PlaneGeometry(1, 1);
329
1848
  disposables.push(geometry);
330
- const mesh = new THREE.InstancedMesh(geometry, material, list.length);
1849
+ const mesh2 = new THREE.InstancedMesh(geometry, material, list2.length);
331
1850
  const matrix = new THREE.Matrix4();
332
1851
  const quaternion = new THREE.Quaternion();
333
1852
  const axis = new THREE.Vector3(0, 1, 0);
334
1853
  const scale = new THREE.Vector3();
335
- list.forEach((entry, index) => {
1854
+ list2.forEach((entry, index) => {
336
1855
  quaternion.setFromAxisAngle(axis, entry.ry);
337
1856
  matrix.compose(new THREE.Vector3(entry.x, entry.y, entry.z), quaternion, scale.set(entry.width, entry.height, 1));
338
- mesh.setMatrixAt(index, matrix);
1857
+ mesh2.setMatrixAt(index, matrix);
339
1858
  });
340
- return mesh;
1859
+ return mesh2;
341
1860
  };
342
1861
  function draw(scene, drawOptions = {}) {
343
1862
  const root = drawOptions.root ?? new THREE.Group();
@@ -590,7 +2109,7 @@ var GameUrbanThree = (() => {
590
2109
  const [h, s, v] = toHsv(c);
591
2110
  return fromHsv(h - amt * 0.03, clamp(s - amt * 0.12, 0, 1), clamp(v + amt * 0.35, 0, 1));
592
2111
  }
593
- function srgbToLinear(v) {
2112
+ function srgbToLinear2(v) {
594
2113
  return v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
595
2114
  }
596
2115
  function linearToSrgb(v) {
@@ -772,7 +2291,7 @@ var GameUrbanThree = (() => {
772
2291
  this.groupTriangles();
773
2292
  const n = this.pos.length / 3;
774
2293
  const colors2 = new Float32Array(n * 3);
775
- for (let i = 0; i < n * 3; i++) colors2[i] = srgbToLinear(this.col[i]);
2294
+ for (let i = 0; i < n * 3; i++) colors2[i] = srgbToLinear2(this.col[i]);
776
2295
  return {
777
2296
  positions: new Float32Array(this.pos),
778
2297
  normals: new Float32Array(this.nor),
@@ -973,16 +2492,16 @@ var GameUrbanThree = (() => {
973
2492
  const f = frames[k];
974
2493
  const r = o.radii[k];
975
2494
  const t = k / (n - 1);
976
- const ring = [];
2495
+ const ring2 = [];
977
2496
  for (let i = 0; i < S; i++) {
978
2497
  const a = i / S * TAU;
979
2498
  const ca = Math.cos(a);
980
2499
  const sa = Math.sin(a);
981
2500
  const dir = [f.n[0] * ca + f.b[0] * sa, f.n[1] * ca + f.b[1] * sa, f.n[2] * ca + f.b[2] * sa];
982
2501
  const rr = r * (1 + (o.rough ? o.rough(k, i) : 0));
983
- ring.push({ p: add(path[k], mul(dir, rr)), nr: dir, c: o.color(t, i / S) });
2502
+ ring2.push({ p: add(path[k], mul(dir, rr)), nr: dir, c: o.color(t, i / S) });
984
2503
  }
985
- rings.push(ring);
2504
+ rings.push(ring2);
986
2505
  }
987
2506
  const sw = (k) => o.sway(k / (n - 1));
988
2507
  if (smooth2) {
@@ -1066,7 +2585,7 @@ var GameUrbanThree = (() => {
1066
2585
  const swayAt = (k) => o.sway(k / (N - 1));
1067
2586
  for (let k = 0; k < N; k++) {
1068
2587
  const f = fr[k];
1069
- const ring = [];
2588
+ const ring2 = [];
1070
2589
  for (let i = 0; i < S; i++) {
1071
2590
  const ridge = i % 2 === 0;
1072
2591
  const rib = i >> 1;
@@ -1076,10 +2595,10 @@ var GameUrbanThree = (() => {
1076
2595
  const rr = radii[k] * (ridge ? 1 + wob : o.valley);
1077
2596
  const p = add(pts[k], mul(dir, rr));
1078
2597
  const c = o.color(Math.min(1, k / (path.length - 1)), ridge, rib);
1079
- ring.push({ p, nr: dir, c });
2598
+ ring2.push({ p, nr: dir, c });
1080
2599
  if (ridge && k < path.length) info.ridges.push({ p, n: dir, t: k / (path.length - 1), rib, ring: k });
1081
2600
  }
1082
- rings.push(ring);
2601
+ rings.push(ring2);
1083
2602
  }
1084
2603
  for (let k = 0; k < N - 1; k++) {
1085
2604
  for (let i = 0; i < S; i++) {
@@ -1401,7 +2920,7 @@ var GameUrbanThree = (() => {
1401
2920
  return { p, r, members: g.members };
1402
2921
  });
1403
2922
  }
1404
- function octa(b, center, radii, color, sway) {
2923
+ function octa(b, center, radii, color2, sway) {
1405
2924
  const pts = [
1406
2925
  [radii[0], 0, 0],
1407
2926
  [0, 0, radii[2]],
@@ -1414,11 +2933,11 @@ var GameUrbanThree = (() => {
1414
2933
  const a = pts[i];
1415
2934
  const c = pts[(i + 1) % 4];
1416
2935
  const out = sub(add(a, c), mul(center, 2));
1417
- b.flatTri(a, c, top, color(0.5), color(0.5), color(1), sway, sway, sway, add(out, [0, 1, 0]));
1418
- b.flatTri(c, a, bot, color(0.5), color(0.5), color(0), sway, sway, sway, add(out, [0, -1, 0]));
2936
+ b.flatTri(a, c, top, color2(0.5), color2(0.5), color2(1), sway, sway, sway, add(out, [0, 1, 0]));
2937
+ b.flatTri(c, a, bot, color2(0.5), color2(0.5), color2(0), sway, sway, sway, add(out, [0, -1, 0]));
1419
2938
  }
1420
2939
  }
1421
- function blossom(b, center, normal, size, color, heart, sway, petals = 5, twist = 0) {
2940
+ function blossom(b, center, normal, size, color2, heart, sway, petals = 5, twist = 0) {
1422
2941
  const f = frameFromAxis(center, normal, twist);
1423
2942
  const peak = toWorld(f, [0, size * 0.3, 0]);
1424
2943
  for (let i = 0; i < petals; i++) {
@@ -1428,7 +2947,7 @@ var GameUrbanThree = (() => {
1428
2947
  const p0 = toWorld(f, [Math.cos(a0) * size * 0.75, 0, Math.sin(a0) * size * 0.75]);
1429
2948
  const p1 = toWorld(f, [Math.cos(a1) * size * 0.75, 0, Math.sin(a1) * size * 0.75]);
1430
2949
  const pm = toWorld(f, [Math.cos(am) * size * 1.1, size * 0.08, Math.sin(am) * size * 1.1]);
1431
- const c = i % 2 ? shade(color, 0.93) : color;
2950
+ const c = i % 2 ? shade(color2, 0.93) : color2;
1432
2951
  b.flatTri(peak, p0, pm, heart, c, lighten(c, 0.4), sway, sway, sway, normal);
1433
2952
  b.flatTri(peak, pm, p1, heart, lighten(c, 0.4), c, sway, sway, sway, normal);
1434
2953
  }
@@ -2318,8 +3837,8 @@ var GameUrbanThree = (() => {
2318
3837
  const nb = ctx.pick(6, 5, 4);
2319
3838
  for (let i = 0; i < nb; i++) {
2320
3839
  const a = i * 2.2 + 0.4;
2321
- const ring = i === 0 ? 0 : moundR * 0.5;
2322
- blobs.push({ c: [Math.cos(a) * ring, moundH * (i === 0 ? 0.55 : 0.28 + 0.07 * (i % 3)), Math.sin(a) * ring], r: moundR * (i === 0 ? 0.62 : 0.52), sq: 0.95 });
3840
+ const ring2 = i === 0 ? 0 : moundR * 0.5;
3841
+ blobs.push({ c: [Math.cos(a) * ring2, moundH * (i === 0 ? 0.55 : 0.28 + 0.07 * (i % 3)), Math.sin(a) * ring2], r: moundR * (i === 0 ? 0.62 : 0.52), sq: 0.95 });
2323
3842
  }
2324
3843
  }
2325
3844
  for (const bl of blobs) clump(ctx.foliage, { center: bl.c, radius: bl.r, squash: bl.sq, detail: ctx.pick(1, 1, 0), seed: rng.seed(), base: dead ? mix(leafBase, [0.42, 0.32, 0.2], 0.7) : dry ? mix(leafBase, [0.55, 0.48, 0.22], 0.45) : mix(leafBase, [0.25, 0.55, 0.22], 0.25), swayK: 0.3, noise: 0.16, flatBottom: 0.8, ao: 0.5 });
@@ -2686,7 +4205,7 @@ var GameUrbanThree = (() => {
2686
4205
  }
2687
4206
  }
2688
4207
  if (o.teeth && o.teeth.count > 0) {
2689
- const { count, color, size } = o.teeth;
4208
+ const { count, color: color2, size } = o.teeth;
2690
4209
  for (let k = 0; k < count; k++) {
2691
4210
  const t0 = 0.12 + 0.7 * k / count;
2692
4211
  const t1 = t0 + 0.05;
@@ -2697,7 +4216,7 @@ var GameUrbanThree = (() => {
2697
4216
  const e1 = sd < 0 ? rows[i1].L : rows[i1].R;
2698
4217
  const out = mul(right, sd * width * size);
2699
4218
  const tip = add(add(mul(add(e0, e1), 0.5), out), mul(d, length * 0.02));
2700
- b.flatTri(e0, tip, e1, color, shade(color, 1.3), color, rows[i0].s, rows[i0].s, rows[i1].s, add(out, n2));
4219
+ b.flatTri(e0, tip, e1, color2, shade(color2, 1.3), color2, rows[i0].s, rows[i0].s, rows[i1].s, add(out, n2));
2701
4220
  }
2702
4221
  }
2703
4222
  }
@@ -2726,8 +4245,8 @@ var GameUrbanThree = (() => {
2726
4245
  const az = i / n * TAU * 1 + rng.range(-0.3, 0.3);
2727
4246
  const el = rng.range(1.2, 1.5);
2728
4247
  const len2 = h * rng.range(0.55, 1) * (ctx.condition === "dead" ? 0.6 : 1);
2729
- const ring = rng.range(0, 0.08);
2730
- const base0 = [Math.cos(az) * ring, 0, Math.sin(az) * ring];
4248
+ const ring2 = rng.range(0, 0.08);
4249
+ const base0 = [Math.cos(az) * ring2, 0, Math.sin(az) * ring2];
2731
4250
  const band = rng.range(0, TAU);
2732
4251
  fleshyLeaf(ctx.foliage, base0, dirFromAngles(az, el), dirFromAngles(az + Math.PI, 0.2), len2, h * 0.16 * rng.range(0.85, 1.15), {
2733
4252
  rows: ctx.pick(7, 4, 2),
@@ -2749,10 +4268,10 @@ var GameUrbanThree = (() => {
2749
4268
  const base = tintFlesh(rgb(spec.color ?? 7315306), ctx.condition);
2750
4269
  const edge = rgb(spec.edgeColor ?? 15261896);
2751
4270
  for (let i = 0; i < n; i++) {
2752
- const ring = i < n * 0.4 ? 0 : i < n * 0.75 ? 1 : 2;
4271
+ const ring2 = i < n * 0.4 ? 0 : i < n * 0.75 ? 1 : 2;
2753
4272
  const az = i * 2.4 + rng.range(-0.2, 0.2);
2754
- const el = [1.15, 0.85, 0.55][ring] + rng.range(-0.1, 0.1);
2755
- const len2 = h * [0.75, 0.95, 0.85][ring] * rng.range(0.9, 1.1);
4273
+ const el = [1.15, 0.85, 0.55][ring2] + rng.range(-0.1, 0.1);
4274
+ const len2 = h * [0.75, 0.95, 0.85][ring2] * rng.range(0.9, 1.1);
2756
4275
  const c = jitter(base, rng, 0.01, 0.06, 0.04);
2757
4276
  fleshyLeaf(ctx.foliage, [0, 0.02, 0], dirFromAngles(az, el), [0, 1, 0], len2, h * 0.26, {
2758
4277
  rows: ctx.pick(3, 2, 2),
@@ -2839,7 +4358,7 @@ var GameUrbanThree = (() => {
2839
4358
  }
2840
4359
  return { collider: null, bendable: true };
2841
4360
  }
2842
- function spineTuft(b, p, n, count, length, color, rng, sway = 0) {
4361
+ function spineTuft(b, p, n, count, length, color2, rng, sway = 0) {
2843
4362
  const nn = norm(n);
2844
4363
  const t1 = perp(nn);
2845
4364
  const t2 = norm(cross(nn, t1));
@@ -2852,15 +4371,15 @@ var GameUrbanThree = (() => {
2852
4371
  const side = mul(norm(cross(dir, nn)), w);
2853
4372
  const left = add(p, side);
2854
4373
  const right = add(p, mul(side, -1));
2855
- b.flatTri(left, right, tip, shade(color, 0.8), shade(color, 0.8), color, sway, sway, sway, nn);
4374
+ b.flatTri(left, right, tip, shade(color2, 0.8), shade(color2, 0.8), color2, sway, sway, sway, nn);
2856
4375
  }
2857
4376
  }
2858
- function areole(b, p, n, size, color, sway = 0) {
4377
+ function areole(b, p, n, size, color2, sway = 0) {
2859
4378
  const nn = norm(n);
2860
4379
  const t1 = mul(perp(nn), size);
2861
4380
  const t2 = mul(norm(cross(nn, perp(nn))), size);
2862
4381
  const c = add(p, mul(nn, size * 0.1));
2863
- b.flatTri(add(c, t1), add(c, add(mul(t1, -0.5), t2)), add(c, add(mul(t1, -0.5), mul(t2, -1))), color, color, color, sway, sway, sway, nn);
4382
+ b.flatTri(add(c, t1), add(c, add(mul(t1, -0.5), t2)), add(c, add(mul(t1, -0.5), mul(t2, -1))), color2, color2, color2, sway, sway, sway, nn);
2864
4383
  }
2865
4384
  function generateColumnarCactus(spec, ctx) {
2866
4385
  const rng = ctx.rng;
@@ -2897,7 +4416,7 @@ var GameUrbanThree = (() => {
2897
4416
  dome: domeSteps,
2898
4417
  color: (t, ridge, rib) => bodyColor(t, ridge, rib + seedK),
2899
4418
  sway: () => 0,
2900
- rough: (ring, rib) => Math.sin(ring * 1.7 + rib * 2.3 + seedK) * 0.03
4419
+ rough: (ring2, rib) => Math.sin(ring2 * 1.7 + rib * 2.3 + seedK) * 0.03
2901
4420
  });
2902
4421
  const last = path[path.length - 1];
2903
4422
  const prev = path[path.length - 2];
@@ -3109,7 +4628,7 @@ var GameUrbanThree = (() => {
3109
4628
  return shade(base, k);
3110
4629
  },
3111
4630
  sway: () => 0,
3112
- rough: (ring, rib) => Math.sin(ring * 2.1 + rib * 1.3) * 0.02
4631
+ rough: (ring2, rib) => Math.sin(ring2 * 2.1 + rib * 1.3) * 0.02
3113
4632
  });
3114
4633
  if (ctx.lod === 0) {
3115
4634
  for (const rd of res.ridges) {
@@ -3264,9 +4783,9 @@ var GameUrbanThree = (() => {
3264
4783
  sides,
3265
4784
  radii,
3266
4785
  color: (t, a) => {
3267
- const ring = 0.84 + 0.16 * Math.cos(t * stemH * scars * TAU);
4786
+ const ring2 = 0.84 + 0.16 * Math.cos(t * stemH * scars * TAU);
3268
4787
  const stripe = 0.94 + 0.06 * Math.cos(a * TAU * 3 + t * 4);
3269
- let c = shade(trunkC, ring * stripe * (0.78 + 0.22 * Math.min(1, t * 6)));
4788
+ let c = shade(trunkC, ring2 * stripe * (0.78 + 0.22 * Math.min(1, t * 6)));
3270
4789
  if (variant === "royal") c = mix(c, [0.62, 0.62, 0.55], 0.25);
3271
4790
  if (dead) c = mix(c, [0.4, 0.37, 0.32], 0.5);
3272
4791
  return c;
@@ -3291,34 +4810,34 @@ var GameUrbanThree = (() => {
3291
4810
  for (let i = 0; i < n; i++) {
3292
4811
  if (ctx.lod === 2 && variant === "fan" && i % 2 === 1) continue;
3293
4812
  const f = srng.fork(i);
3294
- const ring = i % 3;
4813
+ const ring2 = i % 3;
3295
4814
  const az2 = i / n * TAU * 1 + f.range(-0.2, 0.2) + si;
3296
4815
  let elev;
3297
4816
  let droop;
3298
4817
  if (variant === "royal") {
3299
- elev = [1.1, 0.65, 0.2][ring] + f.range(-0.1, 0.1);
3300
- droop = [0.35, 0.65, 1][ring];
4818
+ elev = [1.1, 0.65, 0.2][ring2] + f.range(-0.1, 0.1);
4819
+ droop = [0.35, 0.65, 1][ring2];
3301
4820
  } else if (variant === "date") {
3302
- elev = [1.15, 0.8, 0.45][ring] + f.range(-0.08, 0.08);
3303
- droop = [0.3, 0.5, 0.75][ring];
4821
+ elev = [1.15, 0.8, 0.45][ring2] + f.range(-0.08, 0.08);
4822
+ droop = [0.3, 0.5, 0.75][ring2];
3304
4823
  } else if (variant === "areca") {
3305
- elev = [1.1, 0.8, 0.5][ring] + f.range(-0.1, 0.1);
3306
- droop = [0.45, 0.8, 1.1][ring];
4824
+ elev = [1.1, 0.8, 0.5][ring2] + f.range(-0.1, 0.1);
4825
+ droop = [0.45, 0.8, 1.1][ring2];
3307
4826
  } else if (variant === "fan") {
3308
- elev = [1.3, 0.95, 0.6][ring] + f.range(-0.1, 0.1);
3309
- droop = [0.25, 0.35, 0.5][ring];
4827
+ elev = [1.3, 0.95, 0.6][ring2] + f.range(-0.1, 0.1);
4828
+ droop = [0.25, 0.35, 0.5][ring2];
3310
4829
  } else {
3311
- elev = [1.15, 0.75, 0.38][ring] + f.range(-0.1, 0.1);
3312
- droop = [0.45, 0.78, 1.1][ring];
4830
+ elev = [1.15, 0.75, 0.38][ring2] + f.range(-0.1, 0.1);
4831
+ droop = [0.45, 0.78, 1.1][ring2];
3313
4832
  }
3314
4833
  if (dead) {
3315
4834
  elev = elev * 0.4 - 0.3;
3316
4835
  droop *= 1.4;
3317
4836
  }
3318
4837
  if (dry) droop *= 1.15;
3319
- const len2 = frondL * f.range(0.85, 1.1) * (ring === 2 ? 0.92 : 1);
4838
+ const len2 = frondL * f.range(0.85, 1.1) * (ring2 === 2 ? 0.92 : 1);
3320
4839
  const colA = dead ? mix(c0, deadC, 0.8) : c0;
3321
- const colB = dead ? mix(c1, deadC, 0.7) : ring === 2 && dry ? mix(c1, deadC, 0.4) : c1;
4840
+ const colB = dead ? mix(c1, deadC, 0.7) : ring2 === 2 && dry ? mix(c1, deadC, 0.4) : c1;
3322
4841
  if (variant === "fan") {
3323
4842
  fanFrond(ctx, f, crownTop, az2, elev, len2, droop, colA, colB);
3324
4843
  } else {
@@ -3429,10 +4948,10 @@ var GameUrbanThree = (() => {
3429
4948
  const rows = ctx.pick(9, 6, 3);
3430
4949
  for (let i = 0; i < nLeaves; i++) {
3431
4950
  const f = rng.fork(i);
3432
- const ring = i % 3;
4951
+ const ring2 = i % 3;
3433
4952
  const az = i / nLeaves * TAU * 1 + f.range(-0.25, 0.25);
3434
- const L = H * f.range(0.55, 0.72) * (ring === 2 ? 0.85 : 1);
3435
- const el = [1.15, 0.8, 0.45][ring] + f.range(-0.12, 0.12);
4953
+ const L = H * f.range(0.55, 0.72) * (ring2 === 2 ? 0.85 : 1);
4954
+ const el = [1.15, 0.8, 0.45][ring2] + f.range(-0.12, 0.12);
3436
4955
  const horiz = dirFromAngles(az, 0);
3437
4956
  const petL = L * 0.18;
3438
4957
  const pEnd = [top[0] + horiz[0] * petL * Math.cos(el), top[1] + petL * Math.sin(el), top[2] + horiz[2] * petL * Math.cos(el)];
@@ -3443,7 +4962,7 @@ var GameUrbanThree = (() => {
3443
4962
  const tearSeed = f.range(0, 10);
3444
4963
  leaf(ctx.foliage, pEnd, norm([horiz[0] * Math.cos(el), Math.sin(el), horiz[2] * Math.cos(el)]), [0, 1, 0], L, L * 0.34, c0, c1, {
3445
4964
  rows,
3446
- curl: [0.55, 0.7, 0.8][ring] * (dry ? 1.2 : 1),
4965
+ curl: [0.55, 0.7, 0.8][ring2] * (dry ? 1.2 : 1),
3447
4966
  fold: 0.35,
3448
4967
  swayBase: 0.25,
3449
4968
  swayTip: 1,
@@ -3543,8 +5062,8 @@ var GameUrbanThree = (() => {
3543
5062
  if (hit) return hit;
3544
5063
  const m = Math.max(Math.abs(c[0]), Math.abs(c[1]), Math.abs(c[2]));
3545
5064
  const s = norm(c);
3546
- const sphere = [s[0] * m, s[1] * m, s[2] * m];
3547
- const q = [lerp(c[0], sphere[0], o.round), lerp(c[1], sphere[1], o.round), lerp(c[2], sphere[2], o.round)];
5065
+ const sphere2 = [s[0] * m, s[1] * m, s[2] * m];
5066
+ const q = [lerp(c[0], sphere2[0], o.round), lerp(c[1], sphere2[1], o.round), lerp(c[2], sphere2[2], o.round)];
3548
5067
  const base = [o.center[0] + q[0] * hx, o.center[1] + q[1] * hy, o.center[2] + q[2] * hz];
3549
5068
  const nz = valueNoise3(base[0] * freq, base[1] * freq, base[2] * freq, seed) * 0.75 + valueNoise3(base[0] * freq * 2.3, base[1] * freq * 2.3, base[2] * freq * 2.3, seed + 7) * 0.25;
3550
5069
  const out = norm([q[0] / Math.max(hx, 1e-6), q[1] / Math.max(hy, 1e-6), q[2] / Math.max(hz, 1e-6)]);
@@ -3750,14 +5269,14 @@ var GameUrbanThree = (() => {
3750
5269
  const dark = shade(col, 0.62);
3751
5270
  const apex = [lean * (y / H) ** 2, y + tierH * 0.55, 0];
3752
5271
  const innerR = r * 0.32;
3753
- const ring = (rad, yy, k, ph) => [apex[0] + Math.cos(k / K * TAU + ph) * rad, yy, Math.sin(k / K * TAU + ph) * rad];
5272
+ const ring2 = (rad, yy, k, ph) => [apex[0] + Math.cos(k / K * TAU + ph) * rad, yy, Math.sin(k / K * TAU + ph) * rad];
3754
5273
  const tphase = i * 0.7;
3755
5274
  for (let k = 0; k < K; k++) {
3756
5275
  const jag = k % 2 === 0 ? 1 : 0.78;
3757
- const a0 = ring(innerR, y + tierH * 0.12, k, tphase);
3758
- const a1 = ring(innerR, y + tierH * 0.12, k + 1, tphase);
3759
- const o0 = ring(r * jag, y - tierH * 0.42, k, tphase);
3760
- const o1 = ring(r * (k % 2 === 0 ? 0.78 : 1), y - tierH * 0.42, k + 1, tphase);
5276
+ const a0 = ring2(innerR, y + tierH * 0.12, k, tphase);
5277
+ const a1 = ring2(innerR, y + tierH * 0.12, k + 1, tphase);
5278
+ const o0 = ring2(r * jag, y - tierH * 0.42, k, tphase);
5279
+ const o1 = ring2(r * (k % 2 === 0 ? 0.78 : 1), y - tierH * 0.42, k + 1, tphase);
3761
5280
  const sw = 0.25 + 0.35 * (1 - f);
3762
5281
  foliage.flat(apex, a1, a0, shade(lightTop, 0.85), 0.2, [0, 1, 0]);
3763
5282
  foliage.flatTri(a0, a1, o1, mix(col, lightTop, 0.2), mix(col, lightTop, 0.2), lightTop, 0.2, 0.2, sw, [0, 1, 0]);
@@ -3946,12 +5465,12 @@ var GameUrbanThree = (() => {
3946
5465
  const a1 = (i + 1) / sides * TAU;
3947
5466
  ctx.wood.flatTri([0, soilY, 0], [Math.cos(a1) * rim, soilY, Math.sin(a1) * rim], [Math.cos(a0) * rim, soilY, Math.sin(a0) * rim], shade(soil, 0.9), soil, soil, 0, 0, 0, [0, 1, 0]);
3948
5467
  }
3949
- const inner = generate(spec.plant, { seed: ctx.rng.fork("plant").seed(), lod: ctx.lod, condition: ctx.condition, season: ctx.season });
5468
+ const inner = generate2(spec.plant, { seed: ctx.rng.fork("plant").seed(), lod: ctx.lod, condition: ctx.condition, season: ctx.season });
3950
5469
  const yOff = soilY - 0.02;
3951
5470
  const tagsOf = (part) => {
3952
- const mesh = inner.parts[part];
3953
- if (!mesh) return void 0;
3954
- const tags = new Array(mesh.triangleCount).fill("");
5471
+ const mesh2 = inner.parts[part];
5472
+ if (!mesh2) return void 0;
5473
+ const tags = new Array(mesh2.triangleCount).fill("");
3955
5474
  for (const pc of inner.pieces) {
3956
5475
  if (pc.part !== part) continue;
3957
5476
  if (!ctx.pieceMeta.has(pc.id)) ctx.pieceMeta.set(pc.id, { kind: pc.kind, parent: pc.parent, pivot: [pc.pivot[0], pc.pivot[1] + yOff, pc.pivot[2]], ...pc.radius === void 0 || pc.length === void 0 || !pc.axis || !pc.base ? {} : { geom: { radius: pc.radius, length: pc.length, axis: pc.axis, base: [pc.base[0], pc.base[1] + yOff, pc.base[2]] } } });
@@ -3988,7 +5507,7 @@ var GameUrbanThree = (() => {
3988
5507
  vine: generateVine,
3989
5508
  potted: generatePotted
3990
5509
  };
3991
- function generate(spec, opts = {}) {
5510
+ function generate2(spec, opts = {}) {
3992
5511
  const gen = GENERATORS[spec.archetype];
3993
5512
  if (!gen) throw new Error(`game-vegetation: unknown archetype "${spec.archetype}"`);
3994
5513
  const seed = toSeed(opts.seed);
@@ -4197,29 +5716,29 @@ var GameUrbanThree = (() => {
4197
5716
  }
4198
5717
  const tagBox = /* @__PURE__ */ new Map();
4199
5718
  for (const part of PART_ORDER) {
4200
- const mesh = parts[part];
4201
- if (!mesh) continue;
5719
+ const mesh2 = parts[part];
5720
+ if (!mesh2) continue;
4202
5721
  for (const r of builders[part].ranges) {
4203
5722
  const tag = r.tag || rootId;
4204
- let box = tagBox.get(tag);
4205
- if (!box) tagBox.set(tag, box = { min: [Infinity, Infinity, Infinity], max: [-Infinity, -Infinity, -Infinity] });
5723
+ let box2 = tagBox.get(tag);
5724
+ if (!box2) tagBox.set(tag, box2 = { min: [Infinity, Infinity, Infinity], max: [-Infinity, -Infinity, -Infinity] });
4206
5725
  for (let i = r.indexStart; i < r.indexStart + r.indexCount; i++) {
4207
- const v = mesh.indices[i] * 3;
5726
+ const v = mesh2.indices[i] * 3;
4208
5727
  for (let a = 0; a < 3; a++) {
4209
- const c = mesh.positions[v + a];
4210
- if (c < box.min[a]) box.min[a] = c;
4211
- if (c > box.max[a]) box.max[a] = c;
5728
+ const c = mesh2.positions[v + a];
5729
+ if (c < box2.min[a]) box2.min[a] = c;
5730
+ if (c > box2.max[a]) box2.max[a] = c;
4212
5731
  }
4213
5732
  }
4214
5733
  }
4215
5734
  }
4216
5735
  const fruitHang = /* @__PURE__ */ new Map();
4217
5736
  const PRIORITY = { branch: 0, pad: 0, arm: 0, stem: 1, trunk: 1, crown: 2 };
4218
- for (const [tag, box] of tagBox) {
5737
+ for (const [tag, box2] of tagBox) {
4219
5738
  if (meta.get(tag)?.kind !== "fruit") continue;
4220
- const cx = (box.min[0] + box.max[0]) / 2;
4221
- const cy = (box.min[1] + box.max[1]) / 2;
4222
- const cz = (box.min[2] + box.max[2]) / 2;
5739
+ const cx = (box2.min[0] + box2.max[0]) / 2;
5740
+ const cy = (box2.min[1] + box2.max[1]) / 2;
5741
+ const cz = (box2.min[2] + box2.max[2]) / 2;
4223
5742
  let parent;
4224
5743
  let pbox;
4225
5744
  let bd = Infinity;
@@ -4241,24 +5760,24 @@ var GameUrbanThree = (() => {
4241
5760
  pbox = pb;
4242
5761
  }
4243
5762
  }
4244
- const pc = pbox ? [(pbox.min[0] + pbox.max[0]) / 2, (pbox.min[1] + pbox.max[1]) / 2, (pbox.min[2] + pbox.max[2]) / 2] : [cx, box.max[1], cz];
4245
- const pivot = [Math.min(box.max[0], Math.max(box.min[0], pc[0])), Math.min(box.max[1], Math.max(box.min[1], pc[1])), Math.min(box.max[2], Math.max(box.min[2], pc[2]))];
5763
+ const pc = pbox ? [(pbox.min[0] + pbox.max[0]) / 2, (pbox.min[1] + pbox.max[1]) / 2, (pbox.min[2] + pbox.max[2]) / 2] : [cx, box2.max[1], cz];
5764
+ const pivot = [Math.min(box2.max[0], Math.max(box2.min[0], pc[0])), Math.min(box2.max[1], Math.max(box2.min[1], pc[1])), Math.min(box2.max[2], Math.max(box2.min[2], pc[2]))];
4246
5765
  fruitHang.set(tag, { parent, pivot });
4247
5766
  }
4248
5767
  const idOf = (tag, part) => primary.get(tag) === part ? tag : `${tag}:${part}`;
4249
5768
  const out = [];
4250
5769
  for (const part of PART_ORDER) {
4251
- const mesh = parts[part];
4252
- if (!mesh) continue;
5770
+ const mesh2 = parts[part];
5771
+ if (!mesh2) continue;
4253
5772
  for (const r of builders[part].ranges) {
4254
5773
  const tag = r.tag || rootId;
4255
5774
  const m = meta.get(tag) ?? { kind: "stem" };
4256
5775
  const min = [Infinity, Infinity, Infinity];
4257
5776
  const max = [-Infinity, -Infinity, -Infinity];
4258
5777
  for (let i = r.indexStart; i < r.indexStart + r.indexCount; i++) {
4259
- const v = mesh.indices[i] * 3;
5778
+ const v = mesh2.indices[i] * 3;
4260
5779
  for (let a = 0; a < 3; a++) {
4261
- const c = mesh.positions[v + a];
5780
+ const c = mesh2.positions[v + a];
4262
5781
  if (c < min[a]) min[a] = c;
4263
5782
  if (c > max[a]) max[a] = c;
4264
5783
  }
@@ -4270,7 +5789,7 @@ var GameUrbanThree = (() => {
4270
5789
  else if (m.kind === "crown" || m.kind === "fruit") pivot = [(min[0] + max[0]) / 2, (min[1] + max[1]) / 2, (min[2] + max[2]) / 2];
4271
5790
  else pivot = [(min[0] + max[0]) / 2, min[1], (min[2] + max[2]) / 2];
4272
5791
  if (m.kind === "trunk" || m.kind === "stem" || m.kind === "pot" || m.kind === "soil") pivot = [0, min[1], 0];
4273
- let material = materialKeyForPart(part, mesh.material, condition);
5792
+ let material = materialKeyForPart(part, mesh2.material, condition);
4274
5793
  if (m.kind === "pot") material = "pot";
4275
5794
  else if (m.kind === "soil") material = "soil";
4276
5795
  const parentTag = hang ? hang.parent : m.parent;
@@ -4295,7 +5814,7 @@ var GameUrbanThree = (() => {
4295
5814
 
4296
5815
  // ../game-vegetation/dist/three.js
4297
5816
  var MAX_ACTORS = 4;
4298
- var COMMON = (
5817
+ var COMMON2 = (
4299
5818
  /* glsl */
4300
5819
  `
4301
5820
  #include <common>
@@ -4356,13 +5875,13 @@ gl_Position = projectionMatrix * mvPosition;
4356
5875
  const patch = (m) => {
4357
5876
  m.onBeforeCompile = (shader) => {
4358
5877
  Object.assign(shader.uniforms, uniforms);
4359
- shader.vertexShader = shader.vertexShader.replace("#include <common>", COMMON).replace("#include <project_vertex>", PROJECT);
5878
+ shader.vertexShader = shader.vertexShader.replace("#include <common>", COMMON2).replace("#include <project_vertex>", PROJECT);
4360
5879
  };
4361
5880
  m.customProgramCacheKey = () => "vegetation-sway-v1";
4362
5881
  };
4363
5882
  const flat = opts.flatShading !== false;
4364
5883
  const rough = typeof opts.roughness === "number" ? opts.roughness : 0.85;
4365
- const make = (flatShading, double, roughness) => {
5884
+ const make2 = (flatShading, double, roughness) => {
4366
5885
  const m = new THREE.MeshStandardMaterial({
4367
5886
  vertexColors: true,
4368
5887
  flatShading,
@@ -4373,11 +5892,11 @@ gl_Position = projectionMatrix * mvPosition;
4373
5892
  patch(m);
4374
5893
  return m;
4375
5894
  };
4376
- const wood = make(false, false, 0.95);
4377
- const foliage = make(flat, true, rough);
4378
- const flowers = make(flat, true, 0.7);
4379
- const fruit = make(flat, false, 0.55);
4380
- const spines = make(true, true, 0.8);
5895
+ const wood = make2(false, false, 0.95);
5896
+ const foliage = make2(flat, true, rough);
5897
+ const flowers = make2(flat, true, 0.7);
5898
+ const fruit = make2(flat, false, 0.55);
5899
+ const spines = make2(true, true, 0.8);
4381
5900
  const dir = uniforms.uWindDir.value;
4382
5901
  return {
4383
5902
  materialIds: resolveMaterialIds(opts.materialIds),
@@ -4415,13 +5934,13 @@ gl_Position = projectionMatrix * mvPosition;
4415
5934
  };
4416
5935
  }
4417
5936
  var PART_ORDER2 = ["wood", "foliage", "flowers", "fruit", "spines"];
4418
- function toBufferGeometry(THREE, mesh) {
5937
+ function toBufferGeometry(THREE, mesh2) {
4419
5938
  const g = new THREE.BufferGeometry();
4420
- g.setAttribute("position", new THREE.BufferAttribute(mesh.positions, 3));
4421
- g.setAttribute("normal", new THREE.BufferAttribute(mesh.normals, 3));
4422
- g.setAttribute("color", new THREE.BufferAttribute(mesh.colors, 3));
4423
- g.setAttribute("sway", new THREE.BufferAttribute(mesh.sway, 1));
4424
- g.setIndex(new THREE.BufferAttribute(mesh.indices, 1));
5939
+ g.setAttribute("position", new THREE.BufferAttribute(mesh2.positions, 3));
5940
+ g.setAttribute("normal", new THREE.BufferAttribute(mesh2.normals, 3));
5941
+ g.setAttribute("color", new THREE.BufferAttribute(mesh2.colors, 3));
5942
+ g.setAttribute("sway", new THREE.BufferAttribute(mesh2.sway, 1));
5943
+ g.setIndex(new THREE.BufferAttribute(mesh2.indices, 1));
4425
5944
  g.computeBoundingSphere();
4426
5945
  g.computeBoundingBox();
4427
5946
  return g;
@@ -4435,18 +5954,18 @@ gl_Position = projectionMatrix * mvPosition;
4435
5954
  function meshFor(THREE, data, material, ids, condition) {
4436
5955
  const geo = toBufferGeometry(THREE, data);
4437
5956
  inflate(geo, 0.4);
4438
- const mesh = new THREE.Mesh(geo, material);
4439
- mesh.name = data.kind;
4440
- mesh.userData.damageable = { material: data.material };
4441
- if (ids) mesh.userData.destructionMaterial = ids[materialKeyForPart(data.kind, data.material, condition)];
4442
- return mesh;
5957
+ const mesh2 = new THREE.Mesh(geo, material);
5958
+ mesh2.name = data.kind;
5959
+ mesh2.userData.damageable = { material: data.material };
5960
+ if (ids) mesh2.userData.destructionMaterial = ids[materialKeyForPart(data.kind, data.material, condition)];
5961
+ return mesh2;
4443
5962
  }
4444
- function slicePiece(mesh, indexStart, indexCount, origin) {
5963
+ function slicePiece(mesh2, indexStart, indexCount, origin) {
4445
5964
  const remap = /* @__PURE__ */ new Map();
4446
5965
  const order = [];
4447
5966
  const indices = new Uint32Array(indexCount);
4448
5967
  for (let i = 0; i < indexCount; i++) {
4449
- const v = mesh.indices[indexStart + i];
5968
+ const v = mesh2.indices[indexStart + i];
4450
5969
  let n = remap.get(v);
4451
5970
  if (n === void 0) {
4452
5971
  n = order.length;
@@ -4461,14 +5980,14 @@ gl_Position = projectionMatrix * mvPosition;
4461
5980
  const sway = new Float32Array(order.length);
4462
5981
  for (let i = 0; i < order.length; i++) {
4463
5982
  const v = order[i];
4464
- positions[i * 3] = mesh.positions[v * 3] - origin[0];
4465
- positions[i * 3 + 1] = mesh.positions[v * 3 + 1] - origin[1];
4466
- positions[i * 3 + 2] = mesh.positions[v * 3 + 2] - origin[2];
4467
- normals.set(mesh.normals.subarray(v * 3, v * 3 + 3), i * 3);
4468
- colors2.set(mesh.colors.subarray(v * 3, v * 3 + 3), i * 3);
4469
- sway[i] = mesh.sway[v];
4470
- }
4471
- return { positions, normals, colors: colors2, sway, indices, triangleCount: indexCount / 3, kind: mesh.kind, material: mesh.material };
5983
+ positions[i * 3] = mesh2.positions[v * 3] - origin[0];
5984
+ positions[i * 3 + 1] = mesh2.positions[v * 3 + 1] - origin[1];
5985
+ positions[i * 3 + 2] = mesh2.positions[v * 3 + 2] - origin[2];
5986
+ normals.set(mesh2.normals.subarray(v * 3, v * 3 + 3), i * 3);
5987
+ colors2.set(mesh2.colors.subarray(v * 3, v * 3 + 3), i * 3);
5988
+ sway[i] = mesh2.sway[v];
5989
+ }
5990
+ return { positions, normals, colors: colors2, sway, indices, triangleCount: indexCount / 3, kind: mesh2.kind, material: mesh2.material };
4472
5991
  }
4473
5992
  function createPlantObject(THREE, plant, materials, options = {}) {
4474
5993
  const mats = materials ?? createVegetationMaterials(THREE);
@@ -4480,13 +5999,13 @@ gl_Position = projectionMatrix * mvPosition;
4480
5999
  const src = plant.parts[pc.part];
4481
6000
  if (!src) continue;
4482
6001
  const data = slicePiece(src, pc.indexStart, pc.indexCount, pc.pivot);
4483
- const mesh = meshFor(THREE, data, mats[pc.part]);
4484
- mesh.name = pc.id;
4485
- mesh.position.set(pc.pivot[0], pc.pivot[1], pc.pivot[2]);
6002
+ const mesh2 = meshFor(THREE, data, mats[pc.part]);
6003
+ mesh2.name = pc.id;
6004
+ mesh2.position.set(pc.pivot[0], pc.pivot[1], pc.pivot[2]);
4486
6005
  const id = ids[pc.material];
4487
- mesh.userData.destructionMaterial = id;
4488
- mesh.userData.destructionPart = { id: pc.id, kind: pc.kind, parent: pc.parent, material: id, materialKey: pc.material, ...pc.radius === void 0 ? {} : { radius: pc.radius, length: pc.length, axis: pc.axis, base: pc.base } };
4489
- g.add(mesh);
6006
+ mesh2.userData.destructionMaterial = id;
6007
+ mesh2.userData.destructionPart = { id: pc.id, kind: pc.kind, parent: pc.parent, material: id, materialKey: pc.material, ...pc.radius === void 0 ? {} : { radius: pc.radius, length: pc.length, axis: pc.axis, base: pc.base } };
6008
+ g.add(mesh2);
4490
6009
  }
4491
6010
  g.userData = { collider: plant.collider, bendable: plant.bendable, thorny: plant.thorny, species: plant.species, bounds: plant.bounds, pieces: plant.pieces, separable: true };
4492
6011
  return g;
@@ -7066,16 +8585,16 @@ gl_Position = projectionMatrix * mvPosition;
7066
8585
  };
7067
8586
  }
7068
8587
  function pickForContext(region, context, rng) {
7069
- const list = region.contexts[context];
7070
- if (!list || list.length === 0) throw new Error(`game-vegetation-species: region "${region.id}" has no context "${context}"`);
8588
+ const list2 = region.contexts[context];
8589
+ if (!list2 || list2.length === 0) throw new Error(`game-vegetation-species: region "${region.id}" has no context "${context}"`);
7071
8590
  const next = toNext(rng);
7072
- const total = list.reduce((a, b) => a + b.weight, 0);
8591
+ const total = list2.reduce((a, b) => a + b.weight, 0);
7073
8592
  let r = next() * total;
7074
- for (const it of list) {
8593
+ for (const it of list2) {
7075
8594
  r -= it.weight;
7076
8595
  if (r <= 0) return it.species;
7077
8596
  }
7078
- return list[list.length - 1].species;
8597
+ return list2[list2.length - 1].species;
7079
8598
  }
7080
8599
 
7081
8600
  // src/resolvers.ts
@@ -7107,8 +8626,8 @@ gl_Position = projectionMatrix * mvPosition;
7107
8626
  const key = `${part.id}|${part.seed}`;
7108
8627
  if (!made.has(key)) {
7109
8628
  try {
7110
- const mesh = generate(toSpec(part.id), { seed: part.seed, lod: options.lod ?? 1, condition: options.condition ?? "healthy", season: options.season ?? "spring", species: part.id });
7111
- made.set(key, createPlantObject(THREE, mesh, materials));
8629
+ const mesh2 = generate2(toSpec(part.id), { seed: part.seed, lod: options.lod ?? 1, condition: options.condition ?? "healthy", season: options.season ?? "spring", species: part.id });
8630
+ made.set(key, createPlantObject(THREE, mesh2, materials));
7112
8631
  } catch {
7113
8632
  made.set(key, null);
7114
8633
  }
@@ -7116,8 +8635,8 @@ gl_Position = projectionMatrix * mvPosition;
7116
8635
  return made.get(key)?.clone() ?? null;
7117
8636
  };
7118
8637
  resolve.dispose = () => {
7119
- for (const object of made.values()) {
7120
- object?.traverse((o) => o.geometry?.dispose?.());
8638
+ for (const object2 of made.values()) {
8639
+ object2?.traverse((o) => o.geometry?.dispose?.());
7121
8640
  }
7122
8641
  made.clear();
7123
8642
  };
@@ -7134,8 +8653,8 @@ gl_Position = projectionMatrix * mvPosition;
7134
8653
  CATALOG[def.type] = item;
7135
8654
  return item;
7136
8655
  }
7137
- function registerItems(list) {
7138
- for (const d of list) registerItem(d);
8656
+ function registerItems(list2) {
8657
+ for (const d of list2) registerItem(d);
7139
8658
  }
7140
8659
  var locales = {};
7141
8660
  function registerLocale(code, table) {
@@ -7371,34 +8890,34 @@ gl_Position = projectionMatrix * mvPosition;
7371
8890
  function createSilhouetteBuilder(random) {
7372
8891
  const parts = [];
7373
8892
  const windows = [];
7374
- const pick = (list) => list[Math.floor(random() * list.length)];
8893
+ const pick = (list2) => list2[Math.floor(random() * list2.length)];
7375
8894
  const group = (x, y, z, ry = 0, parent = ROOT) => parts.push({ shape: "group", parent, x, y, z, ry }) - 1;
7376
- const box = (x, y, z, width, height, depth, color, o = {}) => {
7377
- parts.push({ shape: "box", parent: o.parent ?? ROOT, x, y: y + height / 2, z, width, height, depth, color, emissive: o.emissive ?? false, ry: o.ry ?? 0, rz: o.rz ?? 0, radius: boxRadius(width, height, depth) });
8895
+ const box2 = (x, y, z, width, height, depth, color2, o = {}) => {
8896
+ parts.push({ shape: "box", parent: o.parent ?? ROOT, x, y: y + height / 2, z, width, height, depth, color: color2, emissive: o.emissive ?? false, ry: o.ry ?? 0, rz: o.rz ?? 0, radius: boxRadius(width, height, depth) });
7378
8897
  };
7379
- const prism = (x, y, z, width, height, depth, color, parent = ROOT) => {
7380
- parts.push({ shape: "prism", parent, x, y, z, width, height, depth, color, ry: 0 });
8898
+ const prism = (x, y, z, width, height, depth, color2, parent = ROOT) => {
8899
+ parts.push({ shape: "prism", parent, x, y, z, width, height, depth, color: color2, ry: 0 });
7381
8900
  };
7382
- const cylinder = (x, y, z, radiusTop, radiusBottom, height, color, parent = ROOT, segments = 14) => {
7383
- parts.push({ shape: "cylinder", parent, x, y: y + height / 2, z, radiusTop, radiusBottom, height, color, segments });
8901
+ const cylinder2 = (x, y, z, radiusTop, radiusBottom, height, color2, parent = ROOT, segments = 14) => {
8902
+ parts.push({ shape: "cylinder", parent, x, y: y + height / 2, z, radiusTop, radiusBottom, height, color: color2, segments });
7384
8903
  };
7385
- const line = (points, color, parent = ROOT) => {
7386
- parts.push({ shape: "line", parent, points, color });
8904
+ const line = (points, color2, parent = ROOT) => {
8905
+ parts.push({ shape: "line", parent, points, color: color2 });
7387
8906
  };
7388
- const sign = (text, x, y, z, width, height, ry, colors2, parent = ROOT) => {
7389
- parts.push({ shape: "sign", parent, x, y, z, width, height, ry, text, ...colors2 });
8907
+ const sign2 = (text2, x, y, z, width, height, ry, colors2, parent = ROOT) => {
8908
+ parts.push({ shape: "sign", parent, x, y, z, width, height, ry, text: text2, ...colors2 });
7390
8909
  };
7391
- const fence = (x0, z0, x1, z1, height, color, parent = ROOT) => {
8910
+ const fence = (x0, z0, x1, z1, height, color2, parent = ROOT) => {
7392
8911
  const length = Math.hypot(x1 - x0, z1 - z0);
7393
8912
  const count = Math.ceil(length / 3);
7394
8913
  for (let index = 0; index <= count; index++) {
7395
8914
  const t = index / count;
7396
- cylinder(x0 + (x1 - x0) * t, 0, z0 + (z1 - z0) * t, 0.05, 0.05, height, color, parent, 5);
8915
+ cylinder2(x0 + (x1 - x0) * t, 0, z0 + (z1 - z0) * t, 0.05, 0.05, height, color2, parent, 5);
7397
8916
  }
7398
8917
  for (const level of [height * 0.35, height * 0.65, height * 0.98]) line([[x0, level, z0], [x1, level, z1]], 9079950, parent);
7399
8918
  };
7400
8919
  const finish = (footprint) => ({ parts, windows, footprint });
7401
- return { random, pick, group, box, prism, cylinder, line, sign, fence, windows, finish };
8920
+ return { random, pick, group, box: box2, prism, cylinder: cylinder2, line, sign: sign2, fence, windows, finish };
7402
8921
  }
7403
8922
  var STOREY_HEIGHT = 3;
7404
8923
  var SHOP_AWNING_HEIGHT = 4.5;
@@ -7419,8 +8938,8 @@ gl_Position = projectionMatrix * mvPosition;
7419
8938
  const zFace = z + face * (d / 2);
7420
8939
  b.box(x, SHOP_AWNING_HEIGHT, zFace + face * 0.8, w, 0.2, 1.6, 2763308);
7421
8940
  b.box(x, 0, zFace + face * 0.03, w * 0.6, 3.5, 0.05, 4869198);
7422
- const text = b.pick(SHOP_SIGNS);
7423
- b.sign(text, x, 5.3, zFace + face * 0.12, Math.min(w * 0.7, 8), 1, face > 0 ? 0 : Math.PI, { background: "#1a1a1c", foreground: b.pick(SIGN_COLORS) });
8941
+ const text2 = b.pick(SHOP_SIGNS);
8942
+ b.sign(text2, x, 5.3, zFace + face * 0.12, Math.min(w * 0.7, 8), 1, face > 0 ? 0 : Math.PI, { background: "#1a1a1c", foreground: b.pick(SIGN_COLORS) });
7424
8943
  }
7425
8944
  }
7426
8945
  addWindows(b, p, hasShop ? SHOP_AWNING_HEIGHT : 0);
@@ -7455,8 +8974,8 @@ gl_Position = projectionMatrix * mvPosition;
7455
8974
  b.box(0, 3.1, d / 2 + 0.5, w * 0.6, 0.15, 1, 6974054, { parent: g });
7456
8975
  for (const floor of [0, 1]) {
7457
8976
  for (const side of [-1, 1]) {
7458
- const { color, emissive } = pane(b.random() < 0.12);
7459
- b.box(side * w * 0.26, 1.1 + floor * 3, d / 2 + 0.03, 1.1, 1.2, 0.04, color, { parent: g, emissive });
8977
+ const { color: color2, emissive } = pane(b.random() < 0.12);
8978
+ b.box(side * w * 0.26, 1.1 + floor * 3, d / 2 + 0.03, 1.1, 1.2, 0.04, color2, { parent: g, emissive });
7460
8979
  }
7461
8980
  }
7462
8981
  b.box(0, 0, d / 2 + 0.02, 1, 2.1, 0.05, 2759956, { parent: g });
@@ -7469,8 +8988,8 @@ gl_Position = projectionMatrix * mvPosition;
7469
8988
  for (let floor = 0; floor < floors; floor++) {
7470
8989
  const isTop = floor === floors - 1;
7471
8990
  b.box(0, floor * 2.9, 0, w, 2.9, d, isTop && b.random() < 0.5 ? 10119760 : p.color, { parent: g });
7472
- const { color, emissive } = pane(b.random() < 0.1);
7473
- b.box(0, floor * 2.9 + 1.1, d / 2 + 0.03, 1, 1, 0.04, color, { parent: g, emissive });
8991
+ const { color: color2, emissive } = pane(b.random() < 0.1);
8992
+ b.box(0, floor * 2.9 + 1.1, d / 2 + 0.03, 1, 1, 0.04, color2, { parent: g, emissive });
7474
8993
  }
7475
8994
  b.cylinder(w * 0.25, floors * 2.9, -d * 0.2, 0.55, 0.55, 0.9, 2775712, g, 10);
7476
8995
  for (let level = 0; level < floors; level++) b.box(w / 2 + 0.4, level * 2.9, 0, 0.8, 0.18, 1.2, 8026740, { parent: g });
@@ -7489,8 +9008,8 @@ gl_Position = projectionMatrix * mvPosition;
7489
9008
  const u = (index - (count - 1) / 2) * 2.2;
7490
9009
  const isDoor = index === 0 && p.hasDoor;
7491
9010
  const lit = isDoor ? false : b.random() < 0.15;
7492
- const color = isDoor ? 3810326 : lit ? LIT_WINDOW2 : 2767434;
7493
- b.box(u, 1, 5.02, 0.9, 2, 0.05, color, { parent: g, emissive: lit });
9011
+ const color2 = isDoor ? 3810326 : lit ? LIT_WINDOW2 : 2767434;
9012
+ b.box(u, 1, 5.02, 0.9, 2, 0.05, color2, { parent: g, emissive: lit });
7494
9013
  }
7495
9014
  return b.finish({ x: p.x, z: p.z, width: w, depth: 10, height: 6 });
7496
9015
  }
@@ -7501,8 +9020,8 @@ gl_Position = projectionMatrix * mvPosition;
7501
9020
  b.box(0, 0.8, 0, 7, 4.2, 9, p.color ?? 7031344, { parent: g });
7502
9021
  b.prism(0, 5, 0, 7.6, 4, 9.6, 2894376, g);
7503
9022
  b.box(2.2, 5, -1, 0.8, 4.2, 0.8, 5920336, { parent: g });
7504
- const { color, emissive } = pane(b.random() < 0.3);
7505
- b.box(0, 1.6, 4.52, 1.3, 1.3, 0.04, color, { parent: g, emissive });
9023
+ const { color: color2, emissive } = pane(b.random() < 0.3);
9024
+ b.box(0, 1.6, 4.52, 1.3, 1.3, 0.04, color2, { parent: g, emissive });
7506
9025
  b.box(0, 6.2, 4.6, 1, 1, 0.04, DARK_WINDOW2, { parent: g });
7507
9026
  return b.finish({ x: p.x, z: p.z, width: 7.6, depth: 9.6, height: 9 });
7508
9027
  }
@@ -7544,8 +9063,8 @@ gl_Position = projectionMatrix * mvPosition;
7544
9063
  b.prism(0, 3, 0, w + 0.5, 1.6, d + 0.5, p.roofColor ?? DEFAULT_ROOF, g);
7545
9064
  b.box(0, 0, d / 2 + 0.02, 1, 2.1, 0.05, 2759956, { parent: g });
7546
9065
  for (const side of [-1, 1]) {
7547
- const { color, emissive } = pane(b.random() < 0.15);
7548
- b.box(side * w * 0.28, 1.1, d / 2 + 0.03, 1.1, 1.1, 0.04, color, { parent: g, emissive });
9066
+ const { color: color2, emissive } = pane(b.random() < 0.15);
9067
+ b.box(side * w * 0.28, 1.1, d / 2 + 0.03, 1.1, 1.1, 0.04, color2, { parent: g, emissive });
7549
9068
  if (p.isBoarded || b.random() < 0.3) for (const k of [-1, 1]) b.box(side * w * 0.28, 1.5 + k * 0.25, d / 2 + 0.08, 1.3, 0.14, 0.04, 7031338, { parent: g, rz: k * 0.15 });
7550
9069
  }
7551
9070
  return b.finish({ x: p.x, z: p.z, width: w + 0.5, depth: d + 0.5, height: 4.6 });
@@ -12691,8 +14210,8 @@ gl_Position = projectionMatrix * mvPosition;
12691
14210
  });
12692
14211
  return GRM[kind];
12693
14212
  }
12694
- function mkSurf(mesh, W) {
12695
- const geo = mesh.geometry, pos = geo.attributes.position, ix = geo.index, n = ix ? ix.count : pos.count;
14213
+ function mkSurf(mesh2, W) {
14214
+ const geo = mesh2.geometry, pos = geo.attributes.position, ix = geo.index, n = ix ? ix.count : pos.count;
12696
14215
  geo.computeBoundingBox();
12697
14216
  const bb = geo.boundingBox.clone();
12698
14217
  const T = [], BY = {}, BX = {}, Q = 0.02;
@@ -12709,11 +14228,11 @@ gl_Position = projectionMatrix * mvPosition;
12709
14228
  S.hit = function(o, dd) {
12710
14229
  const d = dd.clone().normalize();
12711
14230
  ray.set(o, d);
12712
- const list = Math.abs(d.y) < 0.5 ? BY[Math.floor(o.y / Q)] : BX[Math.floor(o.x / Q)];
12713
- if (!list) return null;
14231
+ const list2 = Math.abs(d.y) < 0.5 ? BY[Math.floor(o.y / Q)] : BX[Math.floor(o.x / Q)];
14232
+ if (!list2) return null;
12714
14233
  let best = 1e9, bt = null;
12715
- for (let k = 0; k < list.length; k++) {
12716
- const t = T[list[k]];
14234
+ for (let k = 0; k < list2.length; k++) {
14235
+ const t = T[list2[k]];
12717
14236
  if (!ray.intersectTriangle(t[0], t[1], t[2], false, tmp)) continue;
12718
14237
  const dist2 = tmp.distanceTo(o);
12719
14238
  if (dist2 < best) {
@@ -12888,8 +14407,8 @@ gl_Position = projectionMatrix * mvPosition;
12888
14407
  t.castShadow = false;
12889
14408
  return t;
12890
14409
  }
12891
- function lineOn(c, Gd, list, r, mat, off) {
12892
- const pts = list.map(function(q) {
14410
+ function lineOn(c, Gd, list2, r, mat, off) {
14411
+ const pts = list2.map(function(q) {
12893
14412
  const s = Gd.at(q[0], q[1]);
12894
14413
  return s.p.addScaledVector(s.n, off || 3e-3);
12895
14414
  });
@@ -12960,8 +14479,8 @@ gl_Position = projectionMatrix * mvPosition;
12960
14479
  const r = sh(q[0]), y = (r[0] + r[1]) / 2 + (q[1] || 0), s = Gd.at(q[0], y), rad = q[2] || Math.min(0.034, (r[1] - r[0]) * 0.4);
12961
14480
  const cup = onSurf(c.cylinder(rad * 1.35, rad * 1.2, 0.012, LH, 0, 0, 0), s, 8e-3);
12962
14481
  cup.castShadow = false;
12963
- const ring = onSurf(c.add(new THREE.TorusGeometry(rad * 1.05, 4e-3, 6, 22), MT.chrome), s, 0.012, new V3(0, 0, 1));
12964
- ring.castShadow = false;
14482
+ const ring2 = onSurf(c.add(new THREE.TorusGeometry(rad * 1.05, 4e-3, 6, 22), MT.chrome), s, 0.012, new V3(0, 0, 1));
14483
+ ring2.castShadow = false;
12965
14484
  const b = onSurf(c.sphere(rad, MT.head, 0, 0, 0, 1, 0.55, 1), s, 0.01);
12966
14485
  b.castShadow = false;
12967
14486
  inner.push(b);
@@ -13578,9 +15097,9 @@ gl_Position = projectionMatrix * mvPosition;
13578
15097
  });
13579
15098
  })();
13580
15099
  const REFLB = CM("#d3d7da", { metalness: 0.35, roughness: 0.28 });
13581
- function dlo(list) {
15100
+ function dlo(list2) {
13582
15101
  return function(def) {
13583
- return list.concat([def[def.length - 1]]);
15102
+ return list2.concat([def[def.length - 1]]);
13584
15103
  };
13585
15104
  }
13586
15105
  function cabTaper(belt, top, x0, L, a) {
@@ -15938,7 +17457,7 @@ gl_Position = projectionMatrix * mvPosition;
15938
17457
  guards: [-1.1, 2],
15939
17458
  lightsY: 0.62
15940
17459
  });
15941
- const box = boxBody(c, { x0: f - len2 - 0.1, x1: -3.72, y0: 0.98, y1: 3.38, W: 2.42, col: "#f1f1ec" });
17460
+ const box2 = boxBody(c, { x0: f - len2 - 0.1, x1: -3.72, y0: 0.98, y1: 3.38, W: 2.42, col: "#f1f1ec" });
15942
17461
  [1, -1].forEach(function(s) {
15943
17462
  c.roundBox(4.6, 0.32, 0.01, 0.01, CM("#1f4fa3", { roughness: 0.5 }), -1, 1.35, s * 1.217);
15944
17463
  c.roundBox(4.6, 0.06, 0.01, 0.01, CM("#e0a21c", { roughness: 0.5 }), -1, 1.56, s * 1.217);
@@ -15969,7 +17488,7 @@ gl_Position = projectionMatrix * mvPosition;
15969
17488
  c2.rod(new V3(-3.86, 0.6, z), new V3(-3.86, 3.5, z), 0.022, kit.steelMaterial);
15970
17489
  });
15971
17490
  });
15972
- void box;
17491
+ void box2;
15973
17492
  }
15974
17493
  );
15975
17494
  fx.register(
@@ -17146,9 +18665,9 @@ gl_Position = projectionMatrix * mvPosition;
17146
18665
  return m;
17147
18666
  }
17148
18667
  function fenestron(c, x, y, R, w, n, mat, bladeMat) {
17149
- const ring = c.add(new THREE.TorusGeometry(R + 0.06, 0.07, 10, 36), mat, x, y, 0);
17150
- ring.scale.z = w / 0.14;
17151
- c.deform.push(ring);
18668
+ const ring2 = c.add(new THREE.TorusGeometry(R + 0.06, 0.07, 10, 36), mat, x, y, 0);
18669
+ ring2.scale.z = w / 0.14;
18670
+ c.deform.push(ring2);
17152
18671
  const duct = c.cylinder(R + 0.01, R + 0.01, w, MT.dark, x, y, 0, "z", { seg: 36, open: true });
17153
18672
  duct.material = CM("#141516", { roughness: 0.8, side: THREE.DoubleSide });
17154
18673
  const g = new THREE.Group();
@@ -17176,7 +18695,7 @@ gl_Position = projectionMatrix * mvPosition;
17176
18695
  );
17177
18696
  }
17178
18697
  c.spin.push({ o: g, axis: "z", v: 22 });
17179
- return { ring, fan: g, duct };
18698
+ return { ring: ring2, fan: g, duct };
17180
18699
  }
17181
18700
  function xRotor(c, x, y, z, R, ch, mat, tip) {
17182
18701
  const g = new THREE.Group();
@@ -18893,16 +20412,16 @@ gl_Position = projectionMatrix * mvPosition;
18893
20412
  function settle(c, tilt, riseOnly) {
18894
20413
  if (tilt) c.vehicle.rotation.z += tilt;
18895
20414
  c.root.updateMatrixWorld(true);
18896
- const inv = new THREE.Matrix4().copy(c.root.matrixWorld).invert(), box = new THREE.Box3(), tmp = new THREE.Box3();
20415
+ const inv = new THREE.Matrix4().copy(c.root.matrixWorld).invert(), box2 = new THREE.Box3(), tmp = new THREE.Box3();
18897
20416
  c.vehicle.traverse(function(ob) {
18898
20417
  if (!ob.isMesh) return;
18899
20418
  for (let q = ob; q && q !== c.vehicle; q = q.parent)
18900
20419
  if (!q.visible || q.userData.decal || q.userData.ghost || q.userData.antena) return;
18901
20420
  if (!ob.geometry.boundingBox) ob.geometry.computeBoundingBox();
18902
20421
  tmp.copy(ob.geometry.boundingBox).applyMatrix4(ob.matrixWorld).applyMatrix4(inv);
18903
- box.union(tmp);
20422
+ box2.union(tmp);
18904
20423
  });
18905
- if (!box.isEmpty() && (!riseOnly || box.min.y < -5e-3)) c.vehicle.position.y -= box.min.y;
20424
+ if (!box2.isEmpty() && (!riseOnly || box2.min.y < -5e-3)) c.vehicle.position.y -= box2.min.y;
18906
20425
  }
18907
20426
  function rollover(c, o) {
18908
20427
  o = o || {};
@@ -19179,10 +20698,10 @@ gl_Position = projectionMatrix * mvPosition;
19179
20698
  function periscope(c, x, y, z, w, label, o) {
19180
20699
  o = o || {};
19181
20700
  const rot = o.rot || 0;
19182
- const box = c.roundBox(0.14, 0.12, w + 0.06, 0.02, o.mat || OLIVE_STEEL, x, y, z, { rot: [0, rot, 0] });
20701
+ const box2 = c.roundBox(0.14, 0.12, w + 0.06, 0.02, o.mat || OLIVE_STEEL, x, y, z, { rot: [0, rot, 0] });
19183
20702
  const gl = c.roundBox(0.02, 0.07, w, 0.01, MT.armorGlass, x + Math.cos(rot) * 0.07, y, z - Math.sin(rot) * 0.07, { rot: [0, rot, 0] });
19184
20703
  if (label) kit.asGlass(c, gl, label, "laminated");
19185
- return box;
20704
+ return box2;
19186
20705
  }
19187
20706
  function sideSeat(c, x0, x1, fl, z, dir, o) {
19188
20707
  o = o || {};
@@ -21510,7 +23029,7 @@ gl_Position = projectionMatrix * mvPosition;
21510
23029
  o = o || {};
21511
23030
  if (kit.lod.on) o = Object.assign({}, o, { seg: Math.max(kit.lod.minLoftSegments, Math.round((o.seg || 24) * kit.lod.loftSegmentFraction)) });
21512
23031
  if (o.smooth) st = resample(st, o.smooth);
21513
- const seg = o.seg || 24, pos = [], idx = [], ring = seg + 1, axisY = o.axis === "y";
23032
+ const seg = o.seg || 24, pos = [], idx = [], ring2 = seg + 1, axisY = o.axis === "y";
21514
23033
  st.forEach(function(s) {
21515
23034
  const a0 = s[1], b0 = s[2], cc = (a0 + b0) / 2, hh = Math.abs(a0 - b0) / 2, w = s[3] / 2, e = 2 / (s[4] || o.p || 2.4), zc = s[5] || 0;
21516
23035
  for (let j = 0; j <= seg; j++) {
@@ -21522,7 +23041,7 @@ gl_Position = projectionMatrix * mvPosition;
21522
23041
  });
21523
23042
  for (let i = 0; i < st.length - 1; i++)
21524
23043
  for (let k = 0; k < seg; k++) {
21525
- const A = i * ring + k, B = A + ring;
23044
+ const A = i * ring2 + k, B = A + ring2;
21526
23045
  if (axisY) idx.push(A, B, A + 1, A + 1, B, B + 1);
21527
23046
  else idx.push(A, A + 1, B, A + 1, B + 1, B);
21528
23047
  }
@@ -21534,7 +23053,7 @@ gl_Position = projectionMatrix * mvPosition;
21534
23053
  if (axisY) pos.push(cc, s[0], s[5] || 0);
21535
23054
  else pos.push(s[0], cc, s[5] || 0);
21536
23055
  for (let k = 0; k < seg; k++) {
21537
- const a = si * ring + k, flip = e === 0 !== axisY;
23056
+ const a = si * ring2 + k, flip = e === 0 !== axisY;
21538
23057
  if (flip) idx.push(ci, a + 1, a);
21539
23058
  else idx.push(ci, a, a + 1);
21540
23059
  }
@@ -21721,8 +23240,8 @@ gl_Position = projectionMatrix * mvPosition;
21721
23240
  });
21722
23241
  return GRM[kind];
21723
23242
  }
21724
- function mkSurf(mesh, W) {
21725
- const geo = mesh.geometry, pos = geo.attributes.position, ix = geo.index, n = ix ? ix.count : pos.count;
23243
+ function mkSurf(mesh2, W) {
23244
+ const geo = mesh2.geometry, pos = geo.attributes.position, ix = geo.index, n = ix ? ix.count : pos.count;
21726
23245
  geo.computeBoundingBox();
21727
23246
  const bb = geo.boundingBox.clone();
21728
23247
  const T = [], BY = {}, BX = {}, Q = 0.02;
@@ -21739,11 +23258,11 @@ gl_Position = projectionMatrix * mvPosition;
21739
23258
  S.hit = function(o, dd) {
21740
23259
  const d = dd.clone().normalize();
21741
23260
  ray.set(o, d);
21742
- const list = Math.abs(d.y) < 0.5 ? BY[Math.floor(o.y / Q)] : BX[Math.floor(o.x / Q)];
21743
- if (!list) return null;
23261
+ const list2 = Math.abs(d.y) < 0.5 ? BY[Math.floor(o.y / Q)] : BX[Math.floor(o.x / Q)];
23262
+ if (!list2) return null;
21744
23263
  let best = 1e9, bt = null;
21745
- for (let k = 0; k < list.length; k++) {
21746
- const t = T[list[k]];
23264
+ for (let k = 0; k < list2.length; k++) {
23265
+ const t = T[list2[k]];
21747
23266
  if (!ray.intersectTriangle(t[0], t[1], t[2], false, tmp)) continue;
21748
23267
  const dist2 = tmp.distanceTo(o);
21749
23268
  if (dist2 < best) {
@@ -21918,8 +23437,8 @@ gl_Position = projectionMatrix * mvPosition;
21918
23437
  t.castShadow = false;
21919
23438
  return t;
21920
23439
  }
21921
- function lineOn(c, Gd, list, r, mat, off) {
21922
- const pts = list.map(function(q) {
23440
+ function lineOn(c, Gd, list2, r, mat, off) {
23441
+ const pts = list2.map(function(q) {
21923
23442
  const s = Gd.at(q[0], q[1]);
21924
23443
  return s.p.addScaledVector(s.n, off || 3e-3);
21925
23444
  });
@@ -21990,8 +23509,8 @@ gl_Position = projectionMatrix * mvPosition;
21990
23509
  const r = sh(q[0]), y = (r[0] + r[1]) / 2 + (q[1] || 0), s = Gd.at(q[0], y), rad = q[2] || Math.min(0.034, (r[1] - r[0]) * 0.4);
21991
23510
  const cup = onSurf(c.cylinder(rad * 1.35, rad * 1.2, 0.012, LH, 0, 0, 0), s, 8e-3);
21992
23511
  cup.castShadow = false;
21993
- const ring = onSurf(c.add(new THREE.TorusGeometry(rad * 1.05, 4e-3, 6, 22), MT.chrome), s, 0.012, new V3(0, 0, 1));
21994
- ring.castShadow = false;
23512
+ const ring2 = onSurf(c.add(new THREE.TorusGeometry(rad * 1.05, 4e-3, 6, 22), MT.chrome), s, 0.012, new V3(0, 0, 1));
23513
+ ring2.castShadow = false;
21995
23514
  const b = onSurf(c.sphere(rad, MT.head, 0, 0, 0, 1, 0.55, 1), s, 0.01);
21996
23515
  b.castShadow = false;
21997
23516
  inner.push(b);
@@ -24068,9 +25587,9 @@ gl_Position = projectionMatrix * mvPosition;
24068
25587
  L.inner.push(m);
24069
25588
  return m;
24070
25589
  }
24071
- function bendSet(list, piv, axis, ang) {
25590
+ function bendSet(list2, piv, axis, ang) {
24072
25591
  const q = new THREE.Quaternion().setFromAxisAngle(axis.clone().normalize(), ang);
24073
- list.forEach(function(m) {
25592
+ list2.forEach(function(m) {
24074
25593
  if (!m.parent) return;
24075
25594
  m.position.sub(piv).applyQuaternion(q).add(piv);
24076
25595
  m.quaternion.premultiply(q);
@@ -25117,7 +26636,7 @@ gl_Position = projectionMatrix * mvPosition;
25117
26636
  );
25118
26637
  [1, -1].forEach(function(s) {
25119
26638
  const z = s * (s > 0 ? 0.44 : 0.4);
25120
- const box = c.roundBox(0.5, 0.38, 0.2, 0.03, aluBox, -0.7, 0.8, z, { def: true }), luggageCase = [box, c.roundBox(0.52, 0.04, 0.21, 0.015, blk, -0.7, 0.98, z)];
26639
+ const box2 = c.roundBox(0.5, 0.38, 0.2, 0.03, aluBox, -0.7, 0.8, z, { def: true }), luggageCase = [box2, c.roundBox(0.52, 0.04, 0.21, 0.015, blk, -0.7, 0.98, z)];
25121
26640
  [-0.88, -0.52].forEach(function(x) {
25122
26641
  luggageCase.push(c.roundBox(0.04, 0.06, 0.03, 0.01, blk, x, 0.95, z + s * 0.1));
25123
26642
  });
@@ -25131,7 +26650,7 @@ gl_Position = projectionMatrix * mvPosition;
25131
26650
  blk,
25132
26651
  { seg: 12 }
25133
26652
  );
25134
- c.part("cargo", s > 0 ? trl("labels.right_pannier") : trl("labels.left_pannier"), box, new V3(-0.7, 0.8, z), 0.6);
26653
+ c.part("cargo", s > 0 ? trl("labels.right_pannier") : trl("labels.left_pannier"), box2, new V3(-0.7, 0.8, z), 0.6);
25135
26654
  c.zone(
25136
26655
  s > 0 ? "case_right" : "case_left",
25137
26656
  s > 0 ? trl("labels.right_pannier") : trl("labels.left_pannier"),
@@ -25779,7 +27298,7 @@ gl_Position = projectionMatrix * mvPosition;
25779
27298
  function sloft(st, o) {
25780
27299
  o = o || {};
25781
27300
  if (kit.lod.on) o = Object.assign({}, o, { seg: Math.max(kit.lod.minLoftSegments, Math.round((o.seg || 24) * kit.lod.loftSegmentFraction)) });
25782
- const seg = o.seg || 24, pos = [], idx = [], ring = seg + 1, axisY = o.axis === "y";
27301
+ const seg = o.seg || 24, pos = [], idx = [], ring2 = seg + 1, axisY = o.axis === "y";
25783
27302
  st.forEach(function(s) {
25784
27303
  const a0 = s[1], b0 = s[2], cc = (a0 + b0) / 2, hh = Math.abs(a0 - b0) / 2, w = s[3] / 2, e = 2 / (s[4] || o.p || 2.4), zc = s[5] || 0;
25785
27304
  for (let j = 0; j <= seg; j++) {
@@ -25791,7 +27310,7 @@ gl_Position = projectionMatrix * mvPosition;
25791
27310
  });
25792
27311
  for (let i = 0; i < st.length - 1; i++)
25793
27312
  for (let k = 0; k < seg; k++) {
25794
- const A = i * ring + k, B = A + ring;
27313
+ const A = i * ring2 + k, B = A + ring2;
25795
27314
  if (axisY) idx.push(A, B, A + 1, A + 1, B, B + 1);
25796
27315
  else idx.push(A, A + 1, B, A + 1, B + 1, B);
25797
27316
  }
@@ -25803,7 +27322,7 @@ gl_Position = projectionMatrix * mvPosition;
25803
27322
  if (axisY) pos.push(cc, s[0], s[5] || 0);
25804
27323
  else pos.push(s[0], cc, s[5] || 0);
25805
27324
  for (let k = 0; k < seg; k++) {
25806
- const a = si * ring + k, flip = e === 0 !== axisY;
27325
+ const a = si * ring2 + k, flip = e === 0 !== axisY;
25807
27326
  if (flip) idx.push(ci, a + 1, a);
25808
27327
  else idx.push(ci, a, a + 1);
25809
27328
  }
@@ -27034,12 +28553,12 @@ gl_Position = projectionMatrix * mvPosition;
27034
28553
  });
27035
28554
  Z.fairing = grab(c, n0).concat([fendF]);
27036
28555
  const HL = lamp(c, { style: "round", x: 0.72, y: 0.84, z: 0, w: 0.19, h: 0.19, d: 0.1, side: 1, label: trl("labels.headlight") });
27037
- const ring = new THREE.Mesh(new THREE.TorusGeometry(0.075, 7e-3, 6, 32), DRL);
27038
- ring.rotation.y = PI / 2;
27039
- ring.position.set(0.052, 0, 0);
27040
- ring.castShadow = false;
27041
- HL.group.add(ring);
27042
- HL.inner.push(ring);
28556
+ const ring2 = new THREE.Mesh(new THREE.TorusGeometry(0.075, 7e-3, 6, 32), DRL);
28557
+ ring2.rotation.y = PI / 2;
28558
+ ring2.position.set(0.052, 0, 0);
28559
+ ring2.castShadow = false;
28560
+ HL.group.add(ring2);
28561
+ HL.inner.push(ring2);
27043
28562
  n0 = c.vehicle.children.length;
27044
28563
  const TK = [
27045
28564
  [0.46, 0.89, 0.76, 0.18, 2.2],
@@ -30088,7 +31607,7 @@ gl_Position = projectionMatrix * mvPosition;
30088
31607
  });
30089
31608
  }
30090
31609
  function roofLining(c, extra) {
30091
- const m = c.meta, yb = (m.glasshouse ? m.glasshouse.belt : m.belt) + 0.25, bb = new THREE.Box3(), sz = new V3(), ce = new V3(), gl = new Set(c.glass), list = [];
31610
+ const m = c.meta, yb = (m.glasshouse ? m.glasshouse.belt : m.belt) + 0.25, bb = new THREE.Box3(), sz = new V3(), ce = new V3(), gl = new Set(c.glass), list2 = [];
30092
31611
  const cols = [m.color, m.roofCol].filter(Boolean).map(function(h) {
30093
31612
  return new THREE.Color(h).getHex();
30094
31613
  });
@@ -30100,14 +31619,14 @@ gl_Position = projectionMatrix * mvPosition;
30100
31619
  bb.copy(o.geometry.boundingBox).applyMatrix4(o.matrix);
30101
31620
  bb.getSize(sz);
30102
31621
  bb.getCenter(ce);
30103
- if (sz.x > 0.5 && sz.z > 0.6 && sz.y < 0.08 && ce.y > yb) list.push(o);
31622
+ if (sz.x > 0.5 && sz.z > 0.6 && sz.y < 0.08 && ce.y > yb) list2.push(o);
30104
31623
  });
30105
31624
  extra = extra || [];
30106
- if (list.length || extra.length)
31625
+ if (list2.length || extra.length)
30107
31626
  c.zone("roof", null, null, null, {
30108
31627
  onBand: function(c2, b) {
30109
31628
  if (b >= 4)
30110
- list.concat(extra).forEach(function(o) {
31629
+ list2.concat(extra).forEach(function(o) {
30111
31630
  o.visible = false;
30112
31631
  });
30113
31632
  }
@@ -31892,7 +33411,7 @@ gl_Position = projectionMatrix * mvPosition;
31892
33411
  }
31893
33412
  return pts;
31894
33413
  }
31895
- const ring = 2 * (m1 + m2) + 1;
33414
+ const ring2 = 2 * (m1 + m2) + 1;
31896
33415
  for (let i = 0; i <= n; i++) {
31897
33416
  const t = i / n, x = o.x0 + t * o.L, P = sec(t), row = [];
31898
33417
  for (let k = P.length - 1; k >= 1; k--) row.push([-P[k][0], P[k][1]]);
@@ -31908,8 +33427,8 @@ gl_Position = projectionMatrix * mvPosition;
31908
33427
  });
31909
33428
  }
31910
33429
  for (let a = 0; a < n; a++)
31911
- for (let k2 = 0; k2 < ring - 1; k2++) {
31912
- const p0 = a * ring + k2, p1 = p0 + ring;
33430
+ for (let k2 = 0; k2 < ring2 - 1; k2++) {
33431
+ const p0 = a * ring2 + k2, p1 = p0 + ring2;
31913
33432
  idx.push(p0, p0 + 1, p1, p0 + 1, p1 + 1, p1);
31914
33433
  }
31915
33434
  const ci = pos.length / 3, sh0 = o.sheer(0), ky0 = o.keel(0);
@@ -31918,7 +33437,7 @@ gl_Position = projectionMatrix * mvPosition;
31918
33437
  const c0 = o.col(o.x0, (sh0[1] + ky0) / 2, 0);
31919
33438
  col.push(c0.r, c0.g, c0.b);
31920
33439
  }
31921
- for (let r = 0; r < ring - 1; r++) idx.push(ci, r + 1, r);
33440
+ for (let r = 0; r < ring2 - 1; r++) idx.push(ci, r + 1, r);
31922
33441
  let g = new THREE.BufferGeometry();
31923
33442
  g.setAttribute("position", new THREE.Float32BufferAttribute(pos, 3));
31924
33443
  if (o.col) g.setAttribute("color", new THREE.Float32BufferAttribute(col, 3));
@@ -31991,7 +33510,7 @@ gl_Position = projectionMatrix * mvPosition;
31991
33510
  function foilGeo(sts, o) {
31992
33511
  o = o || {};
31993
33512
  const n = o.n || 12, up = o.up || "y", pos = [], idx = [];
31994
- let ring;
33513
+ let ring2;
31995
33514
  const cnt = 0;
31996
33515
  function prof(st) {
31997
33516
  const out = [], cam = st.cam || 0;
@@ -32011,14 +33530,14 @@ gl_Position = projectionMatrix * mvPosition;
32011
33530
  }
32012
33531
  sts.forEach(function(st) {
32013
33532
  const P = prof(st);
32014
- ring = P.length;
33533
+ ring2 = P.length;
32015
33534
  P.forEach(function(p) {
32016
33535
  pos.push(p[0], p[1], p[2]);
32017
33536
  });
32018
33537
  });
32019
33538
  for (let a = 0; a < sts.length - 1; a++)
32020
- for (let k = 0; k < ring; k++) {
32021
- const k1 = (k + 1) % ring, p0 = a * ring + k, p1 = (a + 1) * ring + k, q0 = a * ring + k1, q1 = (a + 1) * ring + k1;
33539
+ for (let k = 0; k < ring2; k++) {
33540
+ const k1 = (k + 1) % ring2, p0 = a * ring2 + k, p1 = (a + 1) * ring2 + k, q0 = a * ring2 + k1, q1 = (a + 1) * ring2 + k1;
32022
33541
  idx.push(p0, q0, p1, q0, q1, p1);
32023
33542
  }
32024
33543
  ;
@@ -32026,8 +33545,8 @@ gl_Position = projectionMatrix * mvPosition;
32026
33545
  if (sts[si].noCap) return;
32027
33546
  const ci = pos.length / 3, st = sts[si];
32028
33547
  pos.push(st.p[0] - st.c * 0.35, st.p[1], st.p[2]);
32029
- for (let k = 0; k < ring; k++) {
32030
- const a0 = si * ring + k, a1 = si * ring + (k + 1) % ring;
33548
+ for (let k = 0; k < ring2; k++) {
33549
+ const a0 = si * ring2 + k, a1 = si * ring2 + (k + 1) % ring2;
32031
33550
  if (e === 0) idx.push(ci, a1, a0);
32032
33551
  else idx.push(ci, a0, a1);
32033
33552
  }
@@ -32255,7 +33774,7 @@ gl_Position = projectionMatrix * mvPosition;
32255
33774
  blue: CM("#1d4f9a", { emissive: new THREE.Color("#3a7cff"), emissiveIntensity: 1.4, roughness: 0.3 })
32256
33775
  };
32257
33776
  const HALO = { red: "#ff3a2a", green: "#3aff7a", white: "#fff1d0", amber: "#ffb040", blue: "#4a8cff" };
32258
- function navLight(c, x, y, z, color, label, o) {
33777
+ function navLight(c, x, y, z, color2, label, o) {
32259
33778
  o = o || {};
32260
33779
  const r = o.r || 0.045, g = new THREE.Group();
32261
33780
  g.position.set(x, y, z);
@@ -32265,11 +33784,11 @@ gl_Position = projectionMatrix * mvPosition;
32265
33784
  base.position.y = -r * 0.4;
32266
33785
  if (o.rot) base.rotation.set(o.rot[0], o.rot[1], o.rot[2]);
32267
33786
  g.add(base);
32268
- const bulb = new THREE.Mesh(new THREE.SphereGeometry(r * 0.55, 10, 8), NAV[color] || NAV.white);
33787
+ const bulb = new THREE.Mesh(new THREE.SphereGeometry(r * 0.55, 10, 8), NAV[color2] || NAV.white);
32269
33788
  g.add(bulb);
32270
33789
  const lens = new THREE.Mesh(
32271
33790
  new THREE.SphereGeometry(r, 12, 8),
32272
- color === "red" ? MT.lensRed : color === "amber" ? MT.lensAmber : MT.lens
33791
+ color2 === "red" ? MT.lensRed : color2 === "amber" ? MT.lensAmber : MT.lens
32273
33792
  );
32274
33793
  lens.renderOrder = 3;
32275
33794
  g.add(lens);
@@ -32278,10 +33797,10 @@ gl_Position = projectionMatrix * mvPosition;
32278
33797
  inner: [bulb],
32279
33798
  lens: [lens],
32280
33799
  kind: o.front ? "headlight" : "taillight",
32281
- tint: color === "red" ? "red" : "clear",
33800
+ tint: color2 === "red" ? "red" : "clear",
32282
33801
  broken: false
32283
33802
  };
32284
- if (o.halo !== false) L.halo = [kit.halo(g, 0, 0, 0, o.hs || 0.35, HALO[color] || "#ffffff", 0.5)];
33803
+ if (o.halo !== false) L.halo = [kit.halo(g, 0, 0, 0, o.hs || 0.35, HALO[color2] || "#ffffff", 0.5)];
32285
33804
  c.lights.push(L);
32286
33805
  const pt = c.part(o.front ? "headlight" : "taillight", label, g, new V3(x, y, z), o.reach || 0.5);
32287
33806
  pt.light = L;
@@ -32403,15 +33922,15 @@ gl_Position = projectionMatrix * mvPosition;
32403
33922
  c.roundBox(0.16 * r, 0.03, 0.04, 0.012, MT.chrome, x, y + 0.04, z);
32404
33923
  c.roundBox(0.04, 0.04, 0.03, 0.01, MT.chrome, x, y + 0.02, z);
32405
33924
  }
32406
- function skinOf(c, mesh, keep, mat, o) {
33925
+ function skinOf(c, mesh2, keep, mat, o) {
32407
33926
  o = o || {};
32408
- const g = mesh.geometry.clone(), p = g.attributes.position, sz = o.sz || 1.006, dy = o.dy || 0;
33927
+ const g = mesh2.geometry.clone(), p = g.attributes.position, sz = o.sz || 1.006, dy = o.dy || 0;
32409
33928
  for (let i = 0; i < p.count; i++) {
32410
33929
  p.setZ(i, p.getZ(i) * sz);
32411
33930
  p.setY(i, p.getY(i) + dy * Math.sign(p.getY(i)));
32412
33931
  }
32413
33932
  if (g.attributes.color) g.deleteAttribute("color");
32414
- const m = c.add(g, mat, mesh.position.x, mesh.position.y, mesh.position.z);
33933
+ const m = c.add(g, mat, mesh2.position.x, mesh2.position.y, mesh2.position.z);
32415
33934
  m.castShadow = false;
32416
33935
  kit.cutMesh(m, [keep], 40, o.sub ? { sub: o.sub } : void 0);
32417
33936
  c.deform.push(m);
@@ -34796,26 +36315,26 @@ gl_Position = projectionMatrix * mvPosition;
34796
36315
  const items = [];
34797
36316
  const windows = { lit: [], dark: [] };
34798
36317
  const lights = options.budget ?? createLightBudget(Number.POSITIVE_INFINITY);
34799
- const pick = (list) => list[Math.floor(random() * list.length)];
36318
+ const pick = (list2) => list2[Math.floor(random() * list2.length)];
34800
36319
  const group = (x, y, z, ry = 0, parent = ROOT2) => parts.push({ shape: "group", parent, x, y, z, ry }) - 1;
34801
- const box = (x, y, z, width, height, depth, color, o = {}) => {
34802
- const part = { shape: "box", parent: o.parent ?? ROOT2, x, y: y + height / 2, z, width, height, depth, color, emissive: o.emissive ?? false, ry: o.ry ?? 0, rz: o.rz ?? 0, radius: boxRadius(width, height, depth) };
36320
+ const box2 = (x, y, z, width, height, depth, color2, o = {}) => {
36321
+ const part = { shape: "box", parent: o.parent ?? ROOT2, x, y: y + height / 2, z, width, height, depth, color: color2, emissive: o.emissive ?? false, ry: o.ry ?? 0, rz: o.rz ?? 0, radius: boxRadius(width, height, depth) };
34803
36322
  if (o.style) part.style = o.style;
34804
36323
  parts.push(part);
34805
36324
  };
34806
- const cylinder = (x, y, z, radiusTop, radiusBottom, height, color, parent = ROOT2, segments = DEFAULT_CYLINDER_SEGMENTS, style) => {
34807
- const part = { shape: "cylinder", parent, x, y: y + height / 2, z, radiusTop, radiusBottom, height, color, segments };
36325
+ const cylinder2 = (x, y, z, radiusTop, radiusBottom, height, color2, parent = ROOT2, segments = DEFAULT_CYLINDER_SEGMENTS, style) => {
36326
+ const part = { shape: "cylinder", parent, x, y: y + height / 2, z, radiusTop, radiusBottom, height, color: color2, segments };
34808
36327
  if (style) part.style = style;
34809
36328
  parts.push(part);
34810
36329
  };
34811
- const cone = (x, y, z, radius, height, color, parent = ROOT2, segments = DEFAULT_CONE_SEGMENTS) => {
34812
- parts.push({ shape: "cone", parent, x, y: y + height / 2, z, radius, height, color, segments });
36330
+ const cone2 = (x, y, z, radius, height, color2, parent = ROOT2, segments = DEFAULT_CONE_SEGMENTS) => {
36331
+ parts.push({ shape: "cone", parent, x, y: y + height / 2, z, radius, height, color: color2, segments });
34813
36332
  };
34814
- const prism = (x, y, z, width, height, depth, color, parent = ROOT2, ry = 0) => {
34815
- parts.push({ shape: "prism", parent, x, y, z, width, height, depth, color, ry });
36333
+ const prism = (x, y, z, width, height, depth, color2, parent = ROOT2, ry = 0) => {
36334
+ parts.push({ shape: "prism", parent, x, y, z, width, height, depth, color: color2, ry });
34816
36335
  };
34817
- const plane = (x, y, z, width, depth, color, style, parent = ROOT2) => {
34818
- const part = { shape: "plane", parent, x, y, z, width, depth, color };
36336
+ const plane2 = (x, y, z, width, depth, color2, style, parent = ROOT2) => {
36337
+ const part = { shape: "plane", parent, x, y, z, width, depth, color: color2 };
34819
36338
  if (style) part.style = style;
34820
36339
  parts.push(part);
34821
36340
  };
@@ -34825,11 +36344,11 @@ gl_Position = projectionMatrix * mvPosition;
34825
36344
  const terrain = (grid, parent = ROOT2) => {
34826
36345
  parts.push({ shape: "terrain", parent, grid });
34827
36346
  };
34828
- const crown = (x, y, z, radius, color, parent = ROOT2) => {
34829
- parts.push({ shape: "crown", parent, x, y, z, radius, color });
36347
+ const crown = (x, y, z, radius, color2, parent = ROOT2) => {
36348
+ parts.push({ shape: "crown", parent, x, y, z, radius, color: color2 });
34830
36349
  };
34831
- const line = (points, color, parent = ROOT2) => {
34832
- parts.push({ shape: "line", parent, points, color });
36350
+ const line = (points, color2, parent = ROOT2) => {
36351
+ parts.push({ shape: "line", parent, points, color: color2 });
34833
36352
  };
34834
36353
  const label = (lines, x, y, z, width, height, ry, o, parent = ROOT2) => {
34835
36354
  parts.push({ shape: "label", parent, x, y, z, width, height, ry, lines, background: o.background, foreground: o.foreground, font: o.font ?? DEFAULT_LABEL_FONT, canvas: o.canvas ?? { width: 512, height: 128 } });
@@ -34845,14 +36364,14 @@ gl_Position = projectionMatrix * mvPosition;
34845
36364
  if (o.color !== void 0) part.color = o.color;
34846
36365
  parts.push(part);
34847
36366
  };
34848
- const item = (x, y, z, width, height, depth, color, ry = 0, rx = 0, rz = 0) => {
34849
- items.push({ x, y, z, width, height, depth, color, rx, ry, rz });
36367
+ const item = (x, y, z, width, height, depth, color2, ry = 0, rx = 0, rz = 0) => {
36368
+ items.push({ x, y, z, width, height, depth, color: color2, rx, ry, rz });
34850
36369
  };
34851
- const fence = (x0, z0, x1, z1, height, color = DEFAULT_FENCE_POST) => {
36370
+ const fence = (x0, z0, x1, z1, height, color2 = DEFAULT_FENCE_POST) => {
34852
36371
  const count = Math.ceil(Math.hypot(x1 - x0, z1 - z0) / 3);
34853
36372
  for (let index = 0; index <= count; index++) {
34854
36373
  const t = index / count;
34855
- cylinder(x0 + (x1 - x0) * t, 0, z0 + (z1 - z0) * t, 0.05, 0.05, height, color, ROOT2, 5);
36374
+ cylinder2(x0 + (x1 - x0) * t, 0, z0 + (z1 - z0) * t, 0.05, 0.05, height, color2, ROOT2, 5);
34856
36375
  }
34857
36376
  for (const level of [height * 0.35, height * 0.65, height * 0.98]) line([[x0, level, z0], [x1, level, z1]], DEFAULT_FENCE_WIRE);
34858
36377
  };
@@ -34862,7 +36381,7 @@ gl_Position = projectionMatrix * mvPosition;
34862
36381
  for (const window of silhouette.windows) (window.isLit ? windows.lit : windows.dark).push(window);
34863
36382
  };
34864
36383
  const scene = () => ({ parts, items, windows });
34865
- return { random, pick, lights, parts, items, windows, group, box, cylinder, cone, prism, plane, vault, terrain, crown, line, label, light, spot, place, item, fence, add: add2, scene };
36384
+ return { random, pick, lights, parts, items, windows, group, box: box2, cylinder: cylinder2, cone: cone2, prism, plane: plane2, vault, terrain, crown, line, label, light, spot, place, item, fence, add: add2, scene };
34866
36385
  }
34867
36386
  var on = (o) => o.on ?? true;
34868
36387
  function drawMast(b, mast, x, z, parent = -1) {
@@ -34972,8 +36491,8 @@ gl_Position = projectionMatrix * mvPosition;
34972
36491
  const style = walkStyle(options);
34973
36492
  b.box(x, 0, z, w, style.height, d, style.color, { style: { castShadow: style.castShadow } });
34974
36493
  }
34975
- function drawGround(b, color = DEFAULT_GROUND_COLOR, size = DEFAULT_GROUND_SIZE) {
34976
- b.plane(0, 0, 0, size, size, color);
36494
+ function drawGround(b, color2 = DEFAULT_GROUND_COLOR, size = DEFAULT_GROUND_SIZE) {
36495
+ b.plane(0, 0, 0, size, size, color2);
34977
36496
  }
34978
36497
  function drawStreetGrid(b, block, count, width, options) {
34979
36498
  for (let k = -count; k <= count; k++) {
@@ -34982,6 +36501,85 @@ gl_Position = projectionMatrix * mvPosition;
34982
36501
  }
34983
36502
  for (let i = -count; i < count; i++) for (let j = -count; j < count; j++) drawWalk(b, i * block + block / 2, j * block + block / 2, block - width, block - width, options);
34984
36503
  }
36504
+ var DEFAULT_TOWER_PRESETS = [
36505
+ { id: "office", weight: 0.6, storeyHeight: 4, mechanicalEvery: 20, glass: true, color: 3955315 },
36506
+ { id: "residential", weight: 0.4, storeyHeight: 3.1, mechanicalEvery: 25, glass: false }
36507
+ ];
36508
+ var TOWER_DEFAULTS = { minHeight: 60, maxStoreys: 60, mast: 0.06, lit: 0.12 };
36509
+ var PODIUM = 1.5;
36510
+ var PLANT = 2895667;
36511
+ var PODIUM_COLOR = 2764081;
36512
+ var PARAPET = 2763308;
36513
+ var MAST = 10133670;
36514
+ var MAX_MAST = 25;
36515
+ var GLASS_COLUMN = 1.6;
36516
+ var MASONRY_COLUMN = 3;
36517
+ function pickTowerPreset(b, presets) {
36518
+ const total = presets.reduce((sum2, p) => sum2 + p.weight, 0);
36519
+ let r = b.random() * total;
36520
+ for (const preset of presets) {
36521
+ r -= preset.weight;
36522
+ if (r < 0) return preset;
36523
+ }
36524
+ return presets[presets.length - 1];
36525
+ }
36526
+ var towerStoreys = (height, preset) => Math.max(3, Math.round(height / preset.storeyHeight));
36527
+ function drawTower(b, request, options = {}) {
36528
+ const { x, z, width: w, depth: d, preset } = request;
36529
+ const storeys = towerStoreys(request.height, preset);
36530
+ const h = preset.storeyHeight;
36531
+ const podium = h * PODIUM;
36532
+ const skin = preset.color ?? request.color;
36533
+ const mechanical = /* @__PURE__ */ new Set();
36534
+ if (preset.mechanicalEvery) {
36535
+ for (let k = preset.mechanicalEvery; k < storeys - 1; k += preset.mechanicalEvery) if (storeys - 1 - k >= preset.mechanicalEvery / 2) mechanical.add(k);
36536
+ }
36537
+ mechanical.add(storeys - 1);
36538
+ b.box(x, 0, z, w + 0.6, podium, d + 0.6, PODIUM_COLOR);
36539
+ const shaftTop = (storeys - 1) * h;
36540
+ b.box(x, podium, z, w, shaftTop - podium + (podium - h), d, skin);
36541
+ for (const k of mechanical) {
36542
+ if (k === storeys - 1) b.box(x, k * h + (podium - h), z, w * 0.82, h, d * 0.82, PLANT);
36543
+ else b.box(x, k * h + (podium - h), z, w + 0.4, h, d + 0.4, PLANT);
36544
+ }
36545
+ const top = storeys * h + (podium - h);
36546
+ b.box(x, top, z, w * 0.82 + 0.3, 0.35, d * 0.82 + 0.3, PARAPET);
36547
+ const mast = Math.min(MAX_MAST, top * (options.mast ?? TOWER_DEFAULTS.mast));
36548
+ if (mast > 0.5) b.cylinder(x, top + 0.35, z, 0.12, 0.2, mast, MAST, -1, 6);
36549
+ const column = preset.glass ? GLASS_COLUMN : MASONRY_COLUMN;
36550
+ const winHeight = preset.glass ? h * 0.62 : 1.5;
36551
+ const winWidth = preset.glass ? column * 0.86 : 1.3;
36552
+ const faces = [[0, d / 2 + 0.03, w, 0], [0, -d / 2 - 0.03, w, Math.PI], [w / 2 + 0.03, 0, d, Math.PI / 2], [-w / 2 - 0.03, 0, d, -Math.PI / 2]];
36553
+ for (const [ox, oz, faceWidth, ry] of faces) {
36554
+ const columns = Math.max(1, Math.floor(faceWidth / column));
36555
+ for (let storey = 1; storey < storeys; storey++) {
36556
+ if (mechanical.has(storey)) continue;
36557
+ const y = storey * h + (podium - h) + h * (preset.glass ? 0.2 : 0.35);
36558
+ for (let c = 0; c < columns; c++) {
36559
+ const u = (c - (columns - 1) / 2) * column;
36560
+ const lit = b.random() < request.lit;
36561
+ (lit ? b.windows.lit : b.windows.dark).push({ x: ox ? x + ox : x + u, y, z: ox ? z + u : z + oz, ry, width: winWidth, height: winHeight, isLit: lit });
36562
+ }
36563
+ }
36564
+ }
36565
+ return { x, z, width: w, depth: d, height: top + 0.35 + (mast > 0.5 ? mast : 0), color: request.color };
36566
+ }
36567
+ function createTowerDrawer(towers, plain) {
36568
+ const minHeight = towers.minHeight ?? TOWER_DEFAULTS.minHeight;
36569
+ const presets = towers.presets ?? DEFAULT_TOWER_PRESETS;
36570
+ return (b, x, z, width, depth, height, color2, o = {}) => {
36571
+ if (height < minHeight || presets.length === 0) return plain(b, x, z, width, depth, height, color2, o);
36572
+ const preset = (o.preset ? presets.find((p) => p.id === o.preset) : void 0) ?? pickTowerPreset(b, presets);
36573
+ const top = (preset.maxStoreys ?? towers.maxStoreys ?? TOWER_DEFAULTS.maxStoreys) * preset.storeyHeight;
36574
+ const bottom = (preset.minStoreys ?? 0) * preset.storeyHeight;
36575
+ const request = { x, z, width, depth, height: Math.min(Math.max(height, bottom), top), color: color2, preset, lit: o.lit ?? towers.lit ?? TOWER_DEFAULTS.lit, ...o.use ? { use: o.use } : {}, ...o.storeys ? { storeys: o.storeys } : {} };
36576
+ if (towers.provider) {
36577
+ towers.provider(request, b);
36578
+ return { x, z, width, depth, height: request.height, color: color2 };
36579
+ }
36580
+ return drawTower(b, request, towers);
36581
+ };
36582
+ }
34985
36583
  var DEFAULT_FACADES = [3882821, 4866104, 3489853, 4602426, 4143924, 3093561, 5327167, 5920334];
34986
36584
  var DEFAULT_PARTITIONS = 2;
34987
36585
  var DEFAULT_GAP = 0.08;
@@ -34989,9 +36587,21 @@ gl_Position = projectionMatrix * mvPosition;
34989
36587
  var FOOTPRINT_TRIM = 1.2;
34990
36588
  var FOOTPRINT_JITTER = 1.5;
34991
36589
  var HEIGHT_CURVE = 1.6;
34992
- function drawBuilding(b, x, z, width, depth, height, color, options = {}) {
34993
- b.add(createCityBuilding({ x, z, width, depth, height, color, random: b.random, shopChance: options.shop, litChance: options.lit }));
34994
- return { x, z, width, depth, height, color };
36590
+ var DEFAULT_STOREY_PRESETS = { commercial: "office", residential: "residential" };
36591
+ function drawBuilding(b, x, z, width, depth, height, color2, options = {}) {
36592
+ b.add(createCityBuilding({ x, z, width, depth, height, color: color2, random: b.random, shopChance: options.shop, litChance: options.lit }));
36593
+ return { x, z, width, depth, height, color: color2 };
36594
+ }
36595
+ function pickUse(b, uses) {
36596
+ const entries = Object.entries(uses).filter(([, w]) => w > 0);
36597
+ const total = entries.reduce((sum2, [, w]) => sum2 + w, 0);
36598
+ if (total <= 0) return void 0;
36599
+ let r = b.random() * total;
36600
+ for (const [use, w] of entries) {
36601
+ r -= w;
36602
+ if (r < 0) return use;
36603
+ }
36604
+ return entries[entries.length - 1][0];
34995
36605
  }
34996
36606
  var lerp2 = (a, b, t) => a + (b - a) * t;
34997
36607
  function fillBlock(b, cx, cz, size, minHeight, maxHeight, options = {}) {
@@ -35005,11 +36615,25 @@ gl_Position = projectionMatrix * mvPosition;
35005
36615
  if (b.random() < (options.gap || DEFAULT_GAP)) continue;
35006
36616
  const width = lot - FOOTPRINT_TRIM - b.random() * FOOTPRINT_JITTER;
35007
36617
  const depth = lot - FOOTPRINT_TRIM - b.random() * FOOTPRINT_JITTER;
35008
- const height = lerp2(minHeight, maxHeight, Math.pow(b.random(), HEIGHT_CURVE));
36618
+ const t = Math.pow(b.random(), HEIGHT_CURVE);
36619
+ let height = lerp2(minHeight, maxHeight, t);
36620
+ let extra = null;
36621
+ if (options.floors !== void 0 || options.uses) {
36622
+ const use = options.uses ? pickUse(b, options.uses) : void 0;
36623
+ const presetId = use ? (options.storeyPresets ?? DEFAULT_STOREY_PRESETS)[use] : void 0;
36624
+ const preset = presetId ? (options.presets ?? DEFAULT_TOWER_PRESETS).find((p) => p.id === presetId) : void 0;
36625
+ extra = { use, preset: preset?.id };
36626
+ if (options.floors !== void 0) {
36627
+ const [lo, hi] = typeof options.floors === "number" ? [options.floors, options.floors] : options.floors;
36628
+ const storeys = Math.max(1, Math.round(lerp2(lo, hi, t)));
36629
+ height = storeys * (preset?.storeyHeight ?? options.storeyHeight ?? DEFAULT_TOWER_PRESETS[DEFAULT_TOWER_PRESETS.length - 1].storeyHeight);
36630
+ extra.storeys = storeys;
36631
+ }
36632
+ }
35009
36633
  const x = cx - half + lot * (i + 0.5);
35010
36634
  const z = cz - half + lot * (j + 0.5);
35011
- const color = b.pick(palette);
35012
- placed.push(drawBuilding(b, x, z, width, depth, height, color, options));
36635
+ const color2 = b.pick(palette);
36636
+ placed.push((options.draw ?? drawBuilding)(b, x, z, width, depth, height, color2, extra ? { ...options, ...extra } : options));
35013
36637
  }
35014
36638
  }
35015
36639
  return placed;
@@ -35029,11 +36653,11 @@ gl_Position = projectionMatrix * mvPosition;
35029
36653
  var LABEL_CANVAS = { width: 1024, height: 200 };
35030
36654
  var SANDBAG_LENGTH = 0.56;
35031
36655
  var colors = (c) => ({ ...DEFAULT_PROP_COLORS, ...c });
35032
- function drawFence(b, x0, z0, x1, z1, height, color) {
35033
- b.fence(x0, z0, x1, z1, height, color);
36656
+ function drawFence(b, x0, z0, x1, z1, height, color2) {
36657
+ b.fence(x0, z0, x1, z1, height, color2);
35034
36658
  }
35035
- function drawTent(b, x, z, ry, w, d, color, c) {
35036
- b.prism(x, 0, z, w, w * 0.7, d, color ?? colors(c).tent, -1, ry);
36659
+ function drawTent(b, x, z, ry, w, d, color2, c) {
36660
+ b.prism(x, 0, z, w, w * 0.7, d, color2 ?? colors(c).tent, -1, ry);
35037
36661
  }
35038
36662
  function drawPine(b, x, z, scale = 1, y = 0, c) {
35039
36663
  const palette = colors(c);
@@ -35047,9 +36671,9 @@ gl_Position = projectionMatrix * mvPosition;
35047
36671
  b.cylinder(x, 0, z, 0.12 * scale, 0.18 * scale, 1.8 * scale, palette.trunk);
35048
36672
  b.crown(x, 2.6 * scale, z, 1.3 * scale, b.pick(palette.crowns));
35049
36673
  }
35050
- function drawContainer(b, x, y, z, ry, color) {
36674
+ function drawContainer(b, x, y, z, ry, color2) {
35051
36675
  const group = b.group(x, y, z, ry);
35052
- b.box(0, 0, 0, 2.44, 2.6, 6.06, color, { parent: group, style: { roughness: 0.7, metalness: 0.3 } });
36676
+ b.box(0, 0, 0, 2.44, 2.6, 6.06, color2, { parent: group, style: { roughness: 0.7, metalness: 0.3 } });
35053
36677
  for (let k = -2; k <= 2; k++) b.box(k * 0.5, 0, 3.04, 0.06, 2.6, 0.04, 1710618, { parent: group });
35054
36678
  }
35055
36679
  function drawWatchtower(b, x, z, searchlight = false, c) {
@@ -35061,11 +36685,11 @@ gl_Position = projectionMatrix * mvPosition;
35061
36685
  if (searchlight) b.spot({ x, y: 9, z, color: 15134463, intensity: 2.4, distance: 70, decay: 1.2, angle: 0.35, penumbra: 0.5, target: { x: x + 20, y: 0, z: z + 20 }, sweep: { speed: 0.5, radius: 25 } });
35062
36686
  }
35063
36687
  function drawSandbags(b, x, z, length, along, rows = 3, c) {
35064
- const color = colors(c).sandbags;
36688
+ const color2 = colors(c).sandbags;
35065
36689
  for (let r = 0; r < rows; r++) {
35066
36690
  for (let i = 0; i < length / SANDBAG_LENGTH; i++) {
35067
36691
  const u = -length / 2 + 0.28 + i * SANDBAG_LENGTH + r % 2 * 0.28;
35068
- b.item(along === "x" ? x + u : x, r * 0.21, along === "x" ? z : z + u, along === "x" ? 0.55 : 0.32, 0.22, along === "x" ? 0.32 : 0.55, color);
36692
+ b.item(along === "x" ? x + u : x, r * 0.21, along === "x" ? z : z + u, along === "x" ? 0.55 : 0.32, 0.22, along === "x" ? 0.32 : 0.55, color2);
35069
36693
  }
35070
36694
  }
35071
36695
  }
@@ -35073,8 +36697,8 @@ gl_Position = projectionMatrix * mvPosition;
35073
36697
  const { background, foreground } = colors(c).label;
35074
36698
  b.label([title, subtitle], x, 2.4, z, 22, 4.4, 0, { background, foreground, font: LABEL_FONT, canvas: LABEL_CANVAS });
35075
36699
  }
35076
- function drawWires(b, points, y, color, c) {
35077
- b.line(points.map(([px, pz]) => [px, y, pz]), color ?? colors(c).wire);
36700
+ function drawWires(b, points, y, color2, c) {
36701
+ b.line(points.map(([px, pz]) => [px, y, pz]), color2 ?? colors(c).wire);
35078
36702
  }
35079
36703
  var DEFAULT_WIDTH = 8;
35080
36704
  var DEFAULT_DEPTH = 10;
@@ -35136,18 +36760,18 @@ gl_Position = projectionMatrix * mvPosition;
35136
36760
  else kit.props.tree(b, request.x, request.z, request.scale);
35137
36761
  plan.plants.push(request);
35138
36762
  },
35139
- building: (x, z, width, depth, height, color, o) => {
35140
- plan.buildings.push(kit.block.building(b, x, z, width, depth, height, color, o));
36763
+ building: (x, z, width, depth, height, color2, o) => {
36764
+ plan.buildings.push(kit.block.building(b, x, z, width, depth, height, color2, o));
35141
36765
  }
35142
36766
  };
35143
36767
  composer(context);
35144
- const text = pattern.text[locale] ?? pattern.text[DEFAULT_LOCALE];
35145
- kit.props.label(b, origin.x, origin.z + LABEL_OFFSET_Z, text.title, text.caption);
36768
+ const text2 = pattern.text[locale] ?? pattern.text[DEFAULT_LOCALE];
36769
+ kit.props.label(b, origin.x, origin.z + LABEL_OFFSET_Z, text2.title, text2.caption);
35146
36770
  return plan;
35147
36771
  }
35148
36772
  function patternMarker(pattern, origin, locale = DEFAULT_LOCALE) {
35149
- const text = pattern.text[locale] ?? pattern.text[DEFAULT_LOCALE];
35150
- return { x: origin.x, y: pattern.marker.height, z: origin.z + (pattern.marker.dz ?? 0), label: text.label, detail: text.detail };
36773
+ const text2 = pattern.text[locale] ?? pattern.text[DEFAULT_LOCALE];
36774
+ return { x: origin.x, y: pattern.marker.height, z: origin.z + (pattern.marker.dz ?? 0), label: text2.label, detail: text2.detail };
35151
36775
  }
35152
36776
  var composeDowntown = ({ b, kit, pattern, params, x0, z0, pole, building, vehicle }) => {
35153
36777
  b.plane(x0, 0.02, z0, params.roadLength, pattern.streetWidth, params.roadColor);
@@ -35543,6 +37167,359 @@ gl_Position = projectionMatrix * mvPosition;
35543
37167
  rural: { x: 80, z: 56 },
35544
37168
  military: { x: 160, z: 0 }
35545
37169
  };
37170
+ var DEFAULT_DISTRICT_KINDS = [
37171
+ { id: "downtown", color: "#3b4f7a", zone: "zt", pattern: "financial_district", floors: [20, 60], uses: { commercial: 0.7, residential: 0.3 }, block: 90, text: { "pt-BR": { name: "Centro financeiro", description: "Arranha-c\xE9us de escrit\xF3rio e torres residenciais, de 20 a 60 andares." }, en: { name: "Downtown core", description: "Office and residential towers, 20 to 60 storeys." } } },
37172
+ { id: "commercial_center", color: "#5b6fa3", zone: "zc", pattern: "downtown", floors: [4, 15], uses: { commercial: 0.6, residential: 0.4 }, block: 70, text: { "pt-BR": { name: "Centro comercial", description: "Loja no t\xE9rreo e escrit\xF3rios ou apartamentos em cima, de 4 a 15 andares." }, en: { name: "Commercial centre", description: "Shops on the ground floor, offices or flats above, 4 to 15 storeys." } } },
37173
+ { id: "residential_high", color: "#7a68a6", zone: "zt", pattern: "downtown", floors: [8, 25], uses: { residential: 0.9, commercial: 0.1 }, block: 80, text: { "pt-BR": { name: "Bairro de pr\xE9dios", description: "Condom\xEDnios de pr\xE9dios de 8 a 25 andares com \xE1rea de lazer." }, en: { name: "High-rise residential", description: "Apartment blocks, 8 to 25 storeys." } } },
37174
+ { id: "mixed", color: "#9c7fb8", zone: "zr3_zm", pattern: "downtown", floors: [2, 8], uses: { residential: 0.6, commercial: 0.4 }, block: 60, text: { "pt-BR": { name: "Uso misto", description: "Sobrados, pr\xE9dios baixos e com\xE9rcio de rua, de 2 a 8 andares." }, en: { name: "Mixed use", description: "Townhouses, low blocks and street shops, 2 to 8 storeys." } } },
37175
+ { id: "residential_mid", color: "#d9a35b", zone: "zr2", pattern: "suburb", floors: [1, 4], uses: { residential: 0.95, commercial: 0.05 }, block: 60, text: { "pt-BR": { name: "Residencial m\xE9dio", description: "Casas, sobrados e pr\xE9dios pequenos de at\xE9 4 andares." }, en: { name: "Mid-density residential", description: "Houses and small blocks up to 4 storeys." } } },
37176
+ { id: "residential_low", color: "#e8c477", zone: "zr1", pattern: "suburb", floors: [1, 2], uses: { residential: 1 }, block: 70, text: { "pt-BR": { name: "Residencial de casas", description: "Casas t\xE9rreas e sobrados com quintal e recuo." }, en: { name: "Low-density residential", description: "Houses with yards and setbacks." } } },
37177
+ { id: "residential_gated", color: "#c9d27a", zone: "zr_ap", pattern: "suburb", floors: [1, 3], uses: { residential: 1 }, block: 110, text: { "pt-BR": { name: "Condom\xEDnio fechado", description: "Casas grandes em lotes amplos, muro e portaria." }, en: { name: "Gated community", description: "Large houses on big lots behind a wall and gate." } } },
37178
+ { id: "residential_popular", color: "#d97a5b", zone: "zr2", pattern: "working_class", floors: [1, 3], uses: { residential: 0.9, commercial: 0.1 }, block: 45, text: { "pt-BR": { name: "Bairro popular", description: "Casas geminadas de laje, sem recuo, ruas estreitas." }, en: { name: "Working-class housing", description: "Terraced self-built houses, narrow streets." } } },
37179
+ { id: "historic", color: "#b5835a", zone: "zr3_zm", pattern: "historic_street", floors: [1, 3], uses: { residential: 0.6, commercial: 0.4 }, block: 50, text: { "pt-BR": { name: "Centro hist\xF3rico", description: "Casario colonial colado, rua de pedra, igreja e pra\xE7a." }, en: { name: "Historic quarter", description: "Colonial row houses, cobbled streets, church and square." } } },
37180
+ { id: "institutional", color: "#5aa3a0", zone: "zc", pattern: "downtown", floors: [2, 8], uses: { institutional: 1 }, block: 120, text: { "pt-BR": { name: "Institucional", description: "Hospital, escola, campus, prefeitura, delegacia e quartel de bombeiros." }, en: { name: "Institutional", description: "Hospital, schools, campus, city hall, police and fire stations." } } },
37181
+ { id: "industrial", color: "#7d7d74", zone: "zi", pattern: "industrial_port", floors: [1, 1], uses: { industrial: 1 }, block: 140, text: { "pt-BR": { name: "Distrito industrial", description: "Galp\xF5es de 8 a 12 m, p\xE1tios e chamin\xE9s." }, en: { name: "Industrial district", description: "Sheds 8 to 12 m tall, yards and stacks." } } },
37182
+ { id: "logistics", color: "#96968a", zone: "zi", pattern: "industrial_port", floors: [1, 1], uses: { industrial: 0.8, commercial: 0.2 }, block: 160, text: { "pt-BR": { name: "Distrito log\xEDstico", description: "Centros de distribui\xE7\xE3o, posto de caminhoneiro, p\xE1tio de carretas." }, en: { name: "Logistics", description: "Distribution centres, truck stop, trailer yards." } } },
37183
+ { id: "port", color: "#5f7f8f", zone: "zi", pattern: "industrial_port", floors: [1, 1], uses: { industrial: 1 }, block: 150, text: { "pt-BR": { name: "Porto", description: "Cais, guindastes, cont\xEAineres empilhados e armaz\xE9ns." }, en: { name: "Port", description: "Quays, cranes, stacked containers and warehouses." } } },
37184
+ { id: "rail_yard", color: "#6e6158", zone: "zi", pattern: "railway_village", floors: [1, 1], uses: { industrial: 1 }, block: 150, text: { "pt-BR": { name: "P\xE1tio ferrovi\xE1rio", description: "Linhas, rotunda, oficina e vag\xF5es parados." }, en: { name: "Rail yard", description: "Tracks, roundhouse, workshop and parked wagons." } } },
37185
+ { id: "company_housing", color: "#cfae86", zone: "zr1", pattern: "railway_village", floors: [1, 1], uses: { residential: 1 }, block: 50, text: { "pt-BR": { name: "Vila oper\xE1ria", description: "Casas iguais em fila, da ferrovia ou da f\xE1brica." }, en: { name: "Company housing", description: "Identical row houses of the railway or the mill." } } },
37186
+ { id: "hillside_resort", color: "#a3b58a", zone: "zr1", pattern: "mountain_resort", floors: [1, 2], uses: { residential: 0.7, commercial: 0.3 }, block: 60, text: { "pt-BR": { name: "Serra e pousadas", description: "Chal\xE9s e pousadas na encosta, ruas em curva." }, en: { name: "Hillside resort", description: "Chalets and inns on the slope." } } },
37187
+ { id: "stilt", color: "#7fa3b5", zone: "zr1", pattern: "stilt_village", floors: [1, 1], uses: { residential: 1 }, block: 40, text: { "pt-BR": { name: "Palafitas", description: "Casas sobre estacas na beira da \xE1gua, trapiches." }, en: { name: "Stilt village", description: "Houses on stilts by the water, jetties." } } },
37188
+ { id: "park", color: "#6f9a5e", zone: null, pattern: null, floors: [0, 0], uses: {}, block: 0, text: { "pt-BR": { name: "Parque e pra\xE7a", description: "\xC1rea verde, pra\xE7a, campo de futebol." }, en: { name: "Park", description: "Green space, square, football pitch." } } },
37189
+ { id: "rural", color: "#a7b86d", zone: "za", pattern: "rural", floors: [1, 2], uses: { residential: 0.8, agricultural: 0.2 }, block: 250, text: { "pt-BR": { name: "Ch\xE1caras e s\xEDtios", description: "S\xEDtios com casa sede, horta e pomar." }, en: { name: "Smallholdings", description: "Smallholdings with a farmhouse, garden and orchard." } } },
37190
+ { id: "farmland", color: "#c8c67e", zone: "za", pattern: "rural", floors: [0, 1], uses: { agricultural: 1 }, block: 400, text: { "pt-BR": { name: "Lavoura", description: "Planta\xE7\xE3o, silos e estradas de terra." }, en: { name: "Farmland", description: "Crops, silos and dirt roads." } } },
37191
+ { id: "airfield", color: "#b8b8a8", zone: "za", pattern: "rural", floors: [1, 1], uses: { industrial: 1 }, block: 300, text: { "pt-BR": { name: "Aer\xF3dromo", description: "Pista, hangares e torre." }, en: { name: "Airfield", description: "Runway, hangars and tower." } } },
37192
+ { id: "wild", color: "#4f7a4a", zone: null, pattern: null, floors: [0, 0], uses: {}, block: 0, text: { "pt-BR": { name: "Mata e morro", description: "Mata, morro, \xE1rea sem constru\xE7\xE3o." }, en: { name: "Woods and hills", description: "Woods and hills, unbuilt." } } }
37193
+ ];
37194
+ var DEFAULT_CITY_PLANS = [
37195
+ {
37196
+ id: "metropolis",
37197
+ scale: "metropolis",
37198
+ size: [6e3, 6e3],
37199
+ text: { "pt-BR": { name: "Metr\xF3pole", description: "Centro financeiro de arranha-c\xE9us cercado de com\xE9rcio, bairros de pr\xE9dios, casas, periferia e ind\xFAstria na ferrovia." }, en: { name: "Metropolis", description: "Tower downtown ringed by commerce, high-rise and low-rise neighbourhoods, outskirts and industry on the railway." } },
37200
+ districts: [
37201
+ { id: "outskirts", kind: "rural", rect: [-3e3, -3e3, 3e3, 3e3] },
37202
+ { id: "suburbs_w", kind: "residential_low", rect: [-2600, -2600, -600, 2600] },
37203
+ { id: "suburbs_e", kind: "residential_mid", rect: [600, -2600, 2600, 1e3] },
37204
+ { id: "periphery", kind: "residential_popular", rect: [-3e3, 1200, -800, 3e3] },
37205
+ { id: "gated", kind: "residential_gated", rect: [1400, -3e3, 3e3, -1800] },
37206
+ { id: "industry", kind: "industrial", rect: [600, 1600, 3e3, 3e3] },
37207
+ { id: "logistics", kind: "logistics", rect: [2200, 400, 3e3, 1600] },
37208
+ { id: "mixed", kind: "mixed", rect: [-1600, -1600, 1600, 1400] },
37209
+ { id: "high", kind: "residential_high", rect: [-1400, -1500, -300, -300] },
37210
+ { id: "high2", kind: "residential_high", rect: [300, -1500, 1400, -600] },
37211
+ { id: "center", kind: "commercial_center", rect: [-900, -700, 1100, 800] },
37212
+ { id: "downtown", kind: "downtown", rect: [-400, -400, 600, 400] },
37213
+ { id: "hospital", kind: "institutional", rect: [-1600, 500, -1e3, 1100] },
37214
+ { id: "campus", kind: "institutional", rect: [1300, -500, 2e3, 300] },
37215
+ { id: "park", kind: "park", rect: [-300, 500, 500, 1100] },
37216
+ { id: "hills", kind: "wild", rect: [-3e3, -3e3, -1800, -1800] }
37217
+ ],
37218
+ lines: [
37219
+ { id: "ring", kind: "avenue", width: 30, points: [[-1700, -1700], [1700, -1700], [1700, 1500], [-1700, 1500], [-1700, -1700]] },
37220
+ { id: "main_ns", kind: "avenue", width: 30, points: [[100, -3e3], [100, 3e3]] },
37221
+ { id: "main_ew", kind: "avenue", width: 30, points: [[-3e3, 0], [3e3, 0]] },
37222
+ { id: "rail", kind: "rail", width: 12, points: [[-3e3, 1500], [0, 1500], [3e3, 1300]] },
37223
+ { id: "highway", kind: "highway", width: 24, points: [[-3e3, -2200], [3e3, -2400]] }
37224
+ ],
37225
+ landmarks: [
37226
+ { kind: "station", at: [0, 1500] },
37227
+ { kind: "hospital", at: [-1300, 800] },
37228
+ { kind: "stadium", at: [1700, -100] },
37229
+ { kind: "cathedral", at: [100, 100] },
37230
+ { kind: "mall", at: [800, 600] },
37231
+ { kind: "police", at: [300, -300] },
37232
+ { kind: "fire", at: [-500, 600] },
37233
+ { kind: "gas_station", at: [2400, -2300] }
37234
+ ]
37235
+ },
37236
+ {
37237
+ id: "regional_city",
37238
+ scale: "city",
37239
+ size: [4e3, 4e3],
37240
+ text: { "pt-BR": { name: "Cidade m\xE9dia", description: "Centro comercial com poucos pr\xE9dios altos, bairros de casas e um distrito industrial." }, en: { name: "Regional city", description: "Commercial centre with a few tall buildings, house neighbourhoods and an industrial district." } },
37241
+ districts: [
37242
+ { id: "outskirts", kind: "rural", rect: [-2e3, -2e3, 2e3, 2e3] },
37243
+ { id: "res_w", kind: "residential_low", rect: [-1800, -1600, -300, 1600] },
37244
+ { id: "res_e", kind: "residential_mid", rect: [300, -1600, 1800, 600] },
37245
+ { id: "popular", kind: "residential_popular", rect: [-1800, 800, -500, 1800] },
37246
+ { id: "industry", kind: "industrial", rect: [500, 900, 2e3, 2e3] },
37247
+ { id: "mixed", kind: "mixed", rect: [-800, -800, 900, 700] },
37248
+ { id: "center", kind: "commercial_center", rect: [-400, -400, 500, 350] },
37249
+ { id: "square", kind: "park", rect: [-100, -100, 150, 100] },
37250
+ { id: "hospital", kind: "institutional", rect: [600, -900, 1e3, -500] }
37251
+ ],
37252
+ lines: [
37253
+ { id: "main_ns", kind: "avenue", width: 24, points: [[0, -2e3], [0, 2e3]] },
37254
+ { id: "main_ew", kind: "avenue", width: 24, points: [[-2e3, 0], [2e3, 0]] },
37255
+ { id: "highway", kind: "highway", width: 24, points: [[-2e3, 1600], [2e3, 1500]] }
37256
+ ],
37257
+ landmarks: [
37258
+ { kind: "church", at: [0, 0] },
37259
+ { kind: "hospital", at: [800, -700] },
37260
+ { kind: "school", at: [-600, 300] },
37261
+ { kind: "police", at: [200, -200] },
37262
+ { kind: "market", at: [300, 200] },
37263
+ { kind: "gas_station", at: [1500, 1550] }
37264
+ ]
37265
+ },
37266
+ {
37267
+ id: "port_city",
37268
+ scale: "city",
37269
+ size: [4e3, 3e3],
37270
+ text: { "pt-BR": { name: "Cidade portu\xE1ria", description: "Cais e armaz\xE9ns na margem, centro hist\xF3rico atr\xE1s do porto e bairros subindo o morro." }, en: { name: "Port city", description: "Quays and warehouses on the shore, old town behind the port and neighbourhoods up the hill." } },
37271
+ districts: [
37272
+ { id: "outskirts", kind: "rural", rect: [-2e3, -1500, 2e3, 1500] },
37273
+ { id: "hill", kind: "residential_low", rect: [500, -1500, 2e3, -200] },
37274
+ { id: "res", kind: "residential_mid", rect: [-300, -500, 1300, 900] },
37275
+ { id: "industry", kind: "industrial", rect: [800, 500, 2e3, 1500] },
37276
+ { id: "port", kind: "port", rect: [-2e3, -1300, -900, 1100] },
37277
+ { id: "old_town", kind: "historic", rect: [-900, -800, 200, 400] },
37278
+ { id: "center", kind: "commercial_center", rect: [-500, -400, 200, 100] },
37279
+ { id: "stilts", kind: "stilt", rect: [-2e3, 1e3, -1200, 1500] }
37280
+ ],
37281
+ lines: [
37282
+ { id: "shore", kind: "river", width: 60, points: [[-2e3, -1500], [-1950, 0], [-1900, 1500]] },
37283
+ { id: "main", kind: "avenue", width: 20, points: [[-900, 0], [2e3, 0]] },
37284
+ { id: "rail", kind: "rail", width: 12, points: [[-1200, -1500], [-1e3, -400]] }
37285
+ ],
37286
+ landmarks: [
37287
+ { kind: "harbour_office", at: [-1200, 0] },
37288
+ { kind: "church", at: [-300, -200] },
37289
+ { kind: "market", at: [-600, 200] },
37290
+ { kind: "lighthouse", at: [1600, -1300] },
37291
+ { kind: "hospital", at: [700, 200] }
37292
+ ]
37293
+ },
37294
+ {
37295
+ id: "rail_town",
37296
+ scale: "town",
37297
+ size: [3e3, 3e3],
37298
+ text: { "pt-BR": { name: "Cidade ferrovi\xE1ria", description: "Cidade que cresceu em volta da esta\xE7\xE3o: p\xE1tio, centro velho, vila da companhia." }, en: { name: "Rail town", description: "A town grown around its station: rail yard, old centre, company village." } },
37299
+ districts: [
37300
+ { id: "outskirts", kind: "rural", rect: [-1500, -1500, 1500, 1500] },
37301
+ { id: "res", kind: "residential_low", rect: [500, 500, 1500, 1500] },
37302
+ { id: "logistics", kind: "logistics", rect: [500, -700, 1500, 500] },
37303
+ { id: "popular", kind: "residential_popular", rect: [-1500, 600, -300, 1500] },
37304
+ { id: "company", kind: "company_housing", rect: [-1500, -800, -700, 500] },
37305
+ { id: "yard", kind: "rail_yard", rect: [-700, 0, 500, 500] },
37306
+ { id: "old_center", kind: "historic", rect: [-500, -700, 500, 0] }
37307
+ ],
37308
+ lines: [
37309
+ { id: "rail", kind: "rail", width: 12, points: [[-1500, 250], [1500, 250]] },
37310
+ { id: "highway", kind: "highway", width: 24, points: [[-1500, -100], [1500, -200]] }
37311
+ ],
37312
+ landmarks: [
37313
+ { kind: "station", at: [0, 100] },
37314
+ { kind: "church", at: [0, -400] },
37315
+ { kind: "gas_station", at: [1e3, -150] }
37316
+ ]
37317
+ },
37318
+ {
37319
+ id: "country_town",
37320
+ scale: "town",
37321
+ size: [2400, 2400],
37322
+ text: { "pt-BR": { name: "Cidade do interior", description: "Pra\xE7a da matriz no meio, com\xE9rcio t\xE9rreo em volta, casas, cooperativa e s\xEDtios em volta." }, en: { name: "Country town", description: "Main square with the church, ground-floor shops around, houses, a co-op and smallholdings." } },
37323
+ districts: [
37324
+ { id: "farms", kind: "farmland", rect: [-1200, -1200, 1200, 1200] },
37325
+ { id: "chacaras", kind: "rural", rect: [-900, -900, 900, 900] },
37326
+ { id: "houses", kind: "residential_low", rect: [-600, -600, 600, 500] },
37327
+ { id: "company", kind: "company_housing", rect: [-700, 350, -250, 700] },
37328
+ { id: "coop", kind: "farmland", rect: [350, 500, 900, 900], floors: [1, 1], uses: { agricultural: 0.5, industrial: 0.5 } },
37329
+ { id: "center", kind: "historic", rect: [-250, -250, 250, 250] },
37330
+ { id: "square", kind: "park", rect: [-60, -60, 60, 60] }
37331
+ ],
37332
+ lines: [
37333
+ { id: "main", kind: "street", width: 12, points: [[-1200, 0], [1200, 0]] },
37334
+ { id: "road", kind: "street", width: 8, points: [[0, -1200], [0, 1200]] }
37335
+ ],
37336
+ landmarks: [
37337
+ { kind: "church", at: [0, -70] },
37338
+ { kind: "market", at: [120, 80] },
37339
+ { kind: "school", at: [-300, -200] },
37340
+ { kind: "gas_station", at: [700, 0] },
37341
+ { kind: "silo", at: [600, 700] }
37342
+ ]
37343
+ },
37344
+ {
37345
+ id: "mountain_village",
37346
+ scale: "village",
37347
+ size: [1600, 1200],
37348
+ text: { "pt-BR": { name: "Vila serrana", description: "Largo da igreja, rua das pousadas subindo a serra e chal\xE9s espalhados na encosta." }, en: { name: "Mountain village", description: "Church square, a street of inns up the slope and chalets scattered on the hill." } },
37349
+ districts: [
37350
+ { id: "wild", kind: "wild", rect: [-800, -600, 800, 600] },
37351
+ { id: "slope", kind: "rural", rect: [-700, -500, 700, 500] },
37352
+ { id: "resort", kind: "hillside_resort", rect: [-400, -450, 500, 0] },
37353
+ { id: "square", kind: "historic", rect: [-250, 0, 250, 300] },
37354
+ { id: "station", kind: "rail_yard", rect: [300, 150, 700, 450] }
37355
+ ],
37356
+ lines: [
37357
+ { id: "road", kind: "street", width: 7, points: [[-800, 200], [0, 150], [800, 300]] }
37358
+ ],
37359
+ landmarks: [
37360
+ { kind: "church", at: [0, 150] },
37361
+ { kind: "inn", at: [100, -200] },
37362
+ { kind: "station", at: [500, 300] }
37363
+ ]
37364
+ }
37365
+ ];
37366
+ var DEFAULT_MAX_FLOORS = 60;
37367
+ var DEFAULT_STREET = 10;
37368
+ var DEFAULT_LOCALE2 = "pt-BR";
37369
+ var DEFAULT_STOREY_PRESETS2 = { commercial: "office", residential: "residential" };
37370
+ var text = (by, locale) => by[locale] ?? by[DEFAULT_LOCALE2];
37371
+ var named = (name, locale) => typeof name === "string" || name === void 0 ? name : name[locale] ?? name[DEFAULT_LOCALE2];
37372
+ var mergeLists = (parent, own) => {
37373
+ const list2 = [...parent];
37374
+ for (const item of own) {
37375
+ const at = list2.findIndex((p) => p.id === item.id);
37376
+ if (at >= 0) list2[at] = item;
37377
+ else list2.push(item);
37378
+ }
37379
+ return list2;
37380
+ };
37381
+ function createCities(o) {
37382
+ const maxFloors = o.maxFloors ?? DEFAULT_MAX_FLOORS;
37383
+ const kindEntries = mergeById(DEFAULT_DISTRICT_KINDS, o.packs?.kinds);
37384
+ applyPatches(kindEntries, o.patch?.kinds);
37385
+ removeIds(kindEntries, o.remove?.kinds);
37386
+ const planEntries = mergeById(DEFAULT_CITY_PLANS, o.packs?.plans);
37387
+ applyPatches(planEntries, o.patch?.plans);
37388
+ removeIds(planEntries, o.remove?.plans);
37389
+ const registry = (entries, label) => ({
37390
+ ids: () => [...entries.keys()],
37391
+ list: () => [...entries.values()],
37392
+ has: (id) => entries.has(id),
37393
+ get(id) {
37394
+ const entry = entries.get(id);
37395
+ if (!entry) throw new Error(`Unknown ${label} "${id}"`);
37396
+ return entry;
37397
+ }
37398
+ });
37399
+ const kinds = registry(kindEntries, "district kind");
37400
+ const rawPlans = registry(planEntries, "city plan");
37401
+ const resolvePlan = (plan, seen = []) => {
37402
+ if (seen.includes(plan.id)) throw new Error(`City plan "${plan.id}" extends itself (${[...seen, plan.id].join(" > ")})`);
37403
+ const parent = plan.extends ? resolvePlan(rawPlans.get(plan.extends), [...seen, plan.id]) : void 0;
37404
+ const scale = plan.scale ?? parent?.scale;
37405
+ const size = plan.size ?? parent?.size;
37406
+ const planText = plan.text ?? parent?.text;
37407
+ if (!scale || !size || !planText) throw new Error(`City plan "${plan.id}" has no ${!scale ? "scale" : !size ? "size" : "text"}`);
37408
+ return {
37409
+ id: plan.id,
37410
+ scale,
37411
+ size,
37412
+ text: planText,
37413
+ extends: plan.extends,
37414
+ districts: mergeLists(parent?.districts ?? [], plan.districts ?? []),
37415
+ lines: mergeLists(parent?.lines ?? [], plan.lines ?? []),
37416
+ landmarks: [...parent?.landmarks ?? [], ...plan.landmarks ?? []]
37417
+ };
37418
+ };
37419
+ const plans = {
37420
+ ...rawPlans,
37421
+ /** The plan with everything inherited from the plans it extends. */
37422
+ resolve: (id) => resolvePlan(rawPlans.get(id))
37423
+ };
37424
+ const layout = (planOrId, options = {}) => {
37425
+ const plan = typeof planOrId === "string" ? plans.resolve(planOrId) : resolvePlan(planOrId);
37426
+ const locale = options.locale ?? DEFAULT_LOCALE2;
37427
+ const cap = Math.max(0, options.maxFloors ?? maxFloors);
37428
+ const clamp22 = (n) => Math.min(cap, Math.max(0, n));
37429
+ const districts = plan.districts.map((d) => {
37430
+ const kind = kinds.get(d.kind);
37431
+ const kindText = text(kind.text, locale);
37432
+ const [lo, hi] = d.floors ?? kind.floors;
37433
+ return {
37434
+ id: d.id,
37435
+ kind: kind.id,
37436
+ rect: d.rect,
37437
+ name: named(d.name, locale) ?? kindText.name,
37438
+ description: kindText.description,
37439
+ color: kind.color,
37440
+ zone: kind.zone,
37441
+ pattern: kind.pattern,
37442
+ floors: [clamp22(lo), clamp22(hi)],
37443
+ uses: d.uses ?? kind.uses,
37444
+ block: kind.block
37445
+ };
37446
+ });
37447
+ const inside = (d, x, z) => x >= d.rect[0] && x <= d.rect[2] && z >= d.rect[1] && z <= d.rect[3];
37448
+ const districtAt = (x, z) => {
37449
+ for (let i = districts.length - 1; i >= 0; i--) if (inside(districts[i], x, z)) return districts[i];
37450
+ return void 0;
37451
+ };
37452
+ const blocks = (districtId, bo = {}) => {
37453
+ const d = districts.find((x) => x.id === districtId);
37454
+ if (!d) throw new Error(`City plan "${plan.id}" has no district "${districtId}"`);
37455
+ const street = bo.street ?? DEFAULT_STREET;
37456
+ const cell = d.block;
37457
+ const side = cell - street;
37458
+ if (cell <= 0 || side <= 0) return [];
37459
+ const [x0, z0, x1, z1] = d.rect;
37460
+ const cols = Math.floor((x1 - x0) / cell);
37461
+ const rows = Math.floor((z1 - z0) / cell);
37462
+ const ox = x0 + (x1 - x0 - cols * cell) / 2;
37463
+ const oz = z0 + (z1 - z0 - rows * cell) / 2;
37464
+ const found = [];
37465
+ for (let i = 0; i < cols; i++) {
37466
+ for (let j = 0; j < rows; j++) {
37467
+ const x = ox + cell * (i + 0.5);
37468
+ const z = oz + cell * (j + 0.5);
37469
+ if (districtAt(x, z)?.id !== d.id) continue;
37470
+ found.push({ x, z, size: [side, side], district: d.id, building: { floors: d.floors, uses: d.uses, zone: d.zone, pattern: d.pattern } });
37471
+ }
37472
+ }
37473
+ return found;
37474
+ };
37475
+ const landmarks = plan.landmarks.map((l) => {
37476
+ const { name, ...rest } = l;
37477
+ const label = named(name, locale);
37478
+ return label === void 0 ? rest : { ...rest, name: label };
37479
+ });
37480
+ const planText = text(plan.text, locale);
37481
+ return { id: plan.id, scale: plan.scale, size: plan.size, name: planText.name, description: planText.description, districts, lines: [...plan.lines], landmarks, districtAt, blocks };
37482
+ };
37483
+ const stats2 = (l) => {
37484
+ const xs = [...new Set(l.districts.flatMap((d) => [d.rect[0], d.rect[2]]))].sort((a, b) => a - b);
37485
+ const zs = [...new Set(l.districts.flatMap((d) => [d.rect[1], d.rect[3]]))].sort((a, b) => a - b);
37486
+ const area3 = /* @__PURE__ */ new Map();
37487
+ let total = 0;
37488
+ for (let i = 0; i + 1 < xs.length; i++) {
37489
+ for (let j = 0; j + 1 < zs.length; j++) {
37490
+ const x0 = xs[i];
37491
+ const z0 = zs[j];
37492
+ const w = xs[i + 1] - x0;
37493
+ const d = zs[j + 1] - z0;
37494
+ const owner = l.districtAt(x0 + w / 2, z0 + d / 2);
37495
+ if (!owner) continue;
37496
+ area3.set(owner.kind, (area3.get(owner.kind) ?? 0) + w * d);
37497
+ total += w * d;
37498
+ }
37499
+ }
37500
+ const count = /* @__PURE__ */ new Map();
37501
+ for (const d of l.districts) count.set(d.kind, (count.get(d.kind) ?? 0) + 1);
37502
+ return [...count.keys()].map((kind) => ({ kind, area: area3.get(kind) ?? 0, share: total > 0 ? (area3.get(kind) ?? 0) / total : 0, districts: count.get(kind) ?? 0 })).sort((a, b) => b.area - a.area);
37503
+ };
37504
+ const fillBlock2 = (b, block, options = {}) => {
37505
+ const { floors, uses, pattern } = block.building;
37506
+ if (floors[1] <= 0 || pattern === null) return [];
37507
+ const { shop, ...rest } = options;
37508
+ const storey = options.storeyHeight ?? o.presets.find((p) => p.id === DEFAULT_STOREY_PRESETS2.residential)?.storeyHeight ?? o.presets[0]?.storeyHeight;
37509
+ return o.fill(b, {
37510
+ cx: block.x,
37511
+ cz: block.z,
37512
+ size: Math.min(block.size[0], block.size[1]),
37513
+ minHeight: floors[0] * storey,
37514
+ maxHeight: floors[1] * storey,
37515
+ floors: [floors[0], floors[1]],
37516
+ uses,
37517
+ shop: shop ?? uses.commercial ?? 0,
37518
+ ...rest
37519
+ });
37520
+ };
37521
+ return { maxFloors, kinds, plans, layout, stats: stats2, fillBlock: fillBlock2 };
37522
+ }
35546
37523
  var clamp3 = (v, a, b) => v < a ? a : v > b ? b : v;
35547
37524
  var lerp22 = (a, b, t) => a + (b - a) * t;
35548
37525
  var smoothstep = (a, b, x) => {
@@ -35679,6 +37656,9 @@ gl_Position = projectionMatrix * mvPosition;
35679
37656
  const lightBudget = options.lights?.budget ?? DEFAULT_LIGHT_BUDGET;
35680
37657
  const facades = options.facades ?? DEFAULT_FACADES;
35681
37658
  const propColors = { ...DEFAULT_PROP_COLORS, ...options.props };
37659
+ const towers = options.towers ? options.towers === true ? {} : options.towers : null;
37660
+ const building = (b, x, z, width, depth, height, color2, o) => drawBuilding(b, x, z, width, depth, height, color2, o);
37661
+ const drawBlockBuilding = towers ? createTowerDrawer(towers, building) : building;
35682
37662
  const poles = {
35683
37663
  ids: () => [...entries.keys()],
35684
37664
  list: () => [...entries.values()],
@@ -35700,19 +37680,19 @@ gl_Position = projectionMatrix * mvPosition;
35700
37680
  },
35701
37681
  block: {
35702
37682
  facades,
35703
- building: (b, x, z, width, depth, height, color, o) => drawBuilding(b, x, z, width, depth, height, color, o),
35704
- fill: (b, g) => fillBlock(b, g.cx, g.cz, g.size, g.minHeight, g.maxHeight, { palette: facades, ...g })
37683
+ building: drawBlockBuilding,
37684
+ fill: (b, g) => fillBlock(b, g.cx, g.cz, g.size, g.minHeight, g.maxHeight, { palette: facades, draw: drawBlockBuilding, ...towers?.presets ? { presets: towers.presets } : {}, ...g })
35705
37685
  },
35706
37686
  props: {
35707
37687
  fence: drawFence,
35708
- tent: (b, x, z, ry, w, d, color) => drawTent(b, x, z, ry, w, d, color, propColors),
37688
+ tent: (b, x, z, ry, w, d, color2) => drawTent(b, x, z, ry, w, d, color2, propColors),
35709
37689
  pine: (b, x, z, scale, y) => drawPine(b, x, z, scale, y, propColors),
35710
37690
  tree: (b, x, z, scale) => drawTree(b, x, z, scale, propColors),
35711
37691
  container: drawContainer,
35712
37692
  watchtower: (b, x, z, searchlight) => drawWatchtower(b, x, z, searchlight, propColors),
35713
37693
  sandbags: (b, x, z, length, along, rows) => drawSandbags(b, x, z, length, along, rows, propColors),
35714
37694
  label: (b, x, z, title, subtitle) => drawStripLabel(b, x, z, title, subtitle, propColors),
35715
- wires: (b, points, y, color) => drawWires(b, points, y, color, propColors)
37695
+ wires: (b, points, y, color2) => drawWires(b, points, y, color2, propColors)
35716
37696
  }
35717
37697
  };
35718
37698
  const builder = (o = {}) => createUrbanBuilder(o.random ?? createSilhouetteRandom(o.seed ?? DEFAULT_URBAN_SEED), { budget: createLightBudget(o.budget ?? lightBudget) });
@@ -35744,7 +37724,15 @@ gl_Position = projectionMatrix * mvPosition;
35744
37724
  }
35745
37725
  };
35746
37726
  const terrain = (o) => createTerrain(o);
35747
- return { ...core, poles, builder, neighbourhoods, terrain, drawTerrain };
37727
+ const cities = createCities({
37728
+ maxFloors: options.cities?.maxFloors,
37729
+ packs: { kinds: options.packs?.districtKinds, plans: options.packs?.cityPlans },
37730
+ patch: { kinds: options.patch?.districtKinds, plans: options.patch?.cityPlans },
37731
+ remove: { kinds: options.remove?.districtKinds, plans: options.remove?.cityPlans },
37732
+ presets: towers?.presets ?? DEFAULT_TOWER_PRESETS,
37733
+ fill: core.block.fill
37734
+ });
37735
+ return { ...core, poles, cities, builder, neighbourhoods, terrain, drawTerrain, towers: { enabled: towers !== null, options: towers } };
35748
37736
  }
35749
37737
  var PARKED_VEHICLE_IDS = [...cars, ...suvs, ...pickups.filter((id) => id !== "monster_truck"), ...vans];
35750
37738
  var hashSpot = (x, z, ry) => {
@@ -35762,8 +37750,8 @@ gl_Position = projectionMatrix * mvPosition;
35762
37750
  if (ids.length === 0) throw new Error("The vehicle provider needs at least one vehicle id");
35763
37751
  return (request, b) => {
35764
37752
  const id = options.pick ? options.pick(request, ids) : ids[hashSpot(request.x, request.z, request.ry) % ids.length];
35765
- const color = options.paint ? options.paint(request) : request.color;
35766
- b.place("vehicle", id, request.x, request.z, { ry: request.ry, ...color === void 0 ? {} : { color }, seed: `${id}:${request.x}:${request.z}` });
37753
+ const color2 = options.paint ? options.paint(request) : request.color;
37754
+ b.place("vehicle", id, request.x, request.z, { ry: request.ry, ...color2 === void 0 ? {} : { color: color2 }, seed: `${id}:${request.x}:${request.z}` });
35767
37755
  };
35768
37756
  }
35769
37757
  var DEFAULT_PLANT_CONTEXTS = { tree: "sidewalk_tree", pine: "square" };
@@ -35923,9 +37911,9 @@ gl_Position = projectionMatrix * mvPosition;
35923
37911
  var placements = (parts, kind) => parts.filter((part) => part.shape === "placement" && part.kind === kind);
35924
37912
  var round = (value, digits = 3) => Math.round(value * 10 ** digits) / 10 ** digits;
35925
37913
  var createScenarios2 = (deps) => {
35926
- const { text } = deps;
35927
- const say = (id, ...values) => fill(text.checks[id], ...values);
35928
- const scenario = (id, run) => ({ id, name: text.scenarios[id] ?? id, run });
37914
+ const { text: text2 } = deps;
37915
+ const say = (id, ...values) => fill(text2.checks[id], ...values);
37916
+ const scenario = (id, run) => ({ id, name: text2.scenarios[id] ?? id, run });
35929
37917
  const kit = () => createUrban(deps.urban);
35930
37918
  return [
35931
37919
  scenario("urban_pole_heights", ({ expect }) => {
@@ -36047,7 +38035,7 @@ gl_Position = projectionMatrix * mvPosition;
36047
38035
  var LAMP_SHARES = { pattern: void 0, all: 1, half: 0.5, none: 0 };
36048
38036
  function createUrbanTestModule(options = {}) {
36049
38037
  const locale = (options.locale ?? "pt-BR").toLowerCase().startsWith("en") ? "en" : "pt-BR";
36050
- const text = TEXT[locale];
38038
+ const text2 = TEXT[locale];
36051
38039
  const terrain = options.terrain ?? DEFAULT_TEST_TERRAIN;
36052
38040
  const seed = options.seed ?? 7;
36053
38041
  const benchOrigin = options.benchOrigin ?? [0, -60];
@@ -36105,7 +38093,7 @@ gl_Position = projectionMatrix * mvPosition;
36105
38093
  };
36106
38094
  const item = (id) => ({
36107
38095
  id,
36108
- name: text.items[id] ?? id,
38096
+ name: text2.items[id] ?? id,
36109
38097
  movable: false,
36110
38098
  build: (THREE) => {
36111
38099
  three = THREE;
@@ -36116,9 +38104,9 @@ gl_Position = projectionMatrix * mvPosition;
36116
38104
  },
36117
38105
  spec: (root) => {
36118
38106
  const THREE_ = three;
36119
- const box = new THREE_.Box3().setFromObject(root);
36120
- const size = box.getSize(new THREE_.Vector3());
36121
- const centre = box.getCenter(new THREE_.Vector3());
38107
+ const box2 = new THREE_.Box3().setFromObject(root);
38108
+ const size = box2.getSize(new THREE_.Vector3());
38109
+ const centre = box2.getCenter(new THREE_.Vector3());
36122
38110
  return { name: id, tags: ["structure"], parts: [{ id: "ground", material: "concrete", shape: { kind: "box", size: [Math.max(size.x, 0.1), Math.max(size.y, 0.1), Math.max(size.z, 0.1)] }, position: [centre.x, centre.y, centre.z], anchored: true }] };
36123
38111
  }
36124
38112
  });
@@ -36136,7 +38124,7 @@ gl_Position = projectionMatrix * mvPosition;
36136
38124
  if (!host || !three) return;
36137
38125
  clearBench();
36138
38126
  const kit = kitWith({ lamps: settings.lamps, budget: settings.budget });
36139
- if ((settings.providers === "vehicles" || settings.providers === "both") && !options.vehicles) host.log(text.log.no_vehicles);
38127
+ if ((settings.providers === "vehicles" || settings.providers === "both") && !options.vehicles) host.log(text2.log.no_vehicles);
36140
38128
  const b = kit.builder({ seed: settings.seed });
36141
38129
  const made = kit.neighbourhoods.get(settings.pattern).compose(b, { x: 0, z: 0 }, { locale, providers: providersOf(settings.providers) });
36142
38130
  const document = options.document ?? globalThis.document;
@@ -36145,7 +38133,7 @@ gl_Position = projectionMatrix * mvPosition;
36145
38133
  host.scene.add(drawn.root);
36146
38134
  bench = { root: drawn.root, drawn, plan: made };
36147
38135
  lightsLeft = b.lights.left;
36148
- host.log(fill(text.log.composed, settings.pattern, settings.seed, countObjects(drawn.root), drawn.lights.length), { pattern: settings.pattern, seed: settings.seed });
38136
+ host.log(fill(text2.log.composed, settings.pattern, settings.seed, countObjects(drawn.root), drawn.lights.length), { pattern: settings.pattern, seed: settings.seed });
36149
38137
  helpersShown = "";
36150
38138
  };
36151
38139
  let lightsLeft = settings.budget;
@@ -36155,7 +38143,7 @@ gl_Position = projectionMatrix * mvPosition;
36155
38143
  if (change.id === "compose") composeBench();
36156
38144
  else if (change.id === "clear") {
36157
38145
  clearBench();
36158
- host?.log(text.log.cleared);
38146
+ host?.log(text2.log.cleared);
36159
38147
  }
36160
38148
  return;
36161
38149
  }
@@ -36203,15 +38191,15 @@ gl_Position = projectionMatrix * mvPosition;
36203
38191
  if (!entry.root.parent) continue;
36204
38192
  entry.root.updateMatrixWorld(true);
36205
38193
  if (host.bench.layer("bounds")) {
36206
- const box = new three.Box3().setFromObject(entry.root);
38194
+ const box2 = new three.Box3().setFromObject(entry.root);
36207
38195
  const inverse = new three.Matrix4().copy(entry.root.matrixWorld).invert();
36208
- box.applyMatrix4(inverse);
36209
- mark(entry.root, new three.Box3Helper(box, 6737151));
38196
+ box2.applyMatrix4(inverse);
38197
+ mark(entry.root, new three.Box3Helper(box2, 6737151));
36210
38198
  }
36211
38199
  if (host.bench.layer("light_radius")) {
36212
38200
  for (const light of entry.drawn.lights) {
36213
38201
  if (!light.isPointLight && !light.isSpotLight) continue;
36214
- const reach = new three.Mesh(new three.SphereGeometry(Math.min(light.distance || 10, 60), 14, 8), new three.MeshBasicMaterial({ color: light.color ?? 16764006, wireframe: true, transparent: true, opacity: 0.25 }));
38202
+ const reach = new three.Mesh(new (shapeGeometries(three)).SphereGeometry(Math.min(light.distance || 10, 60), 14, 8), new three.MeshBasicMaterial({ color: light.color ?? 16764006, wireframe: true, transparent: true, opacity: 0.25 }));
36215
38203
  reach.position.copy(light.position);
36216
38204
  mark(light.parent ?? entry.root, reach);
36217
38205
  }
@@ -36244,7 +38232,7 @@ gl_Position = projectionMatrix * mvPosition;
36244
38232
  }
36245
38233
  return { drawCalls, triangles: Math.round(triangles), lights, instances };
36246
38234
  };
36247
- const area = () => {
38235
+ const area3 = () => {
36248
38236
  const placements2 = [];
36249
38237
  poleIds.forEach((id, index) => placements2.push({ item: `pole_${id}`, position: [(index - (poleIds.length - 1) / 2) * POLE_GAP, 0, POLE_ROW_Z], rotationY: 0 }));
36250
38238
  placements2.push({ item: "street_grid", position: [-230, 0, -200], rotationY: 0 });
@@ -36259,8 +38247,8 @@ gl_Position = projectionMatrix * mvPosition;
36259
38247
  };
36260
38248
  return {
36261
38249
  id: URBAN_AREA_ID,
36262
- name: text.name,
36263
- group: text.group,
38250
+ name: text2.name,
38251
+ group: text2.group,
36264
38252
  get placed() {
36265
38253
  return placed;
36266
38254
  },
@@ -36270,25 +38258,25 @@ gl_Position = projectionMatrix * mvPosition;
36270
38258
  },
36271
38259
  items: () => itemIds.map(item),
36272
38260
  scenarios: () => createScenarios2({ text: TEXT[locale], urban: options.urban, terrain }),
36273
- area,
38261
+ area: area3,
36274
38262
  controls: () => [
36275
- { kind: "choice", id: "pattern", name: text.controls.pattern, hint: text.hints.pattern, options: patternIds.map((id) => ({ id, name: text.items[id] ?? id })), default: patternIds.includes("downtown") ? "downtown" : patternIds[0] },
36276
- { kind: "number", id: "seed", name: text.controls.seed, hint: text.hints.seed, min: 0, max: 99999, step: 1, default: seed },
36277
- { kind: "choice", id: "lamps", name: text.controls.lamps, hint: text.hints.lamps, options: Object.keys(LAMP_SHARES).map((id) => ({ id, name: text.options.lamps[id] })), default: "pattern" },
36278
- { kind: "number", id: "budget", name: text.controls.budget, hint: text.hints.budget, min: 0, max: 64, step: 1, default: 16 },
36279
- { kind: "choice", id: "providers", name: text.controls.providers, hint: text.hints.providers, options: ["none", "plants", "vehicles", "both"].map((id) => ({ id, name: text.options.providers[id] })), default: "none" },
36280
- { kind: "action", id: "compose", name: text.actions.compose },
36281
- { kind: "action", id: "clear", name: text.actions.clear }
38263
+ { kind: "choice", id: "pattern", name: text2.controls.pattern, hint: text2.hints.pattern, options: patternIds.map((id) => ({ id, name: text2.items[id] ?? id })), default: patternIds.includes("downtown") ? "downtown" : patternIds[0] },
38264
+ { kind: "number", id: "seed", name: text2.controls.seed, hint: text2.hints.seed, min: 0, max: 99999, step: 1, default: seed },
38265
+ { kind: "choice", id: "lamps", name: text2.controls.lamps, hint: text2.hints.lamps, options: Object.keys(LAMP_SHARES).map((id) => ({ id, name: text2.options.lamps[id] })), default: "pattern" },
38266
+ { kind: "number", id: "budget", name: text2.controls.budget, hint: text2.hints.budget, min: 0, max: 64, step: 1, default: 16 },
38267
+ { kind: "choice", id: "providers", name: text2.controls.providers, hint: text2.hints.providers, options: ["none", "plants", "vehicles", "both"].map((id) => ({ id, name: text2.options.providers[id] })), default: "none" },
38268
+ { kind: "action", id: "compose", name: text2.actions.compose },
38269
+ { kind: "action", id: "clear", name: text2.actions.clear }
36282
38270
  ],
36283
- layers: () => ["light_radius", "bounds"].map((id) => ({ id, name: text.layers[id], default: false })),
38271
+ layers: () => ["light_radius", "bounds"].map((id) => ({ id, name: text2.layers[id], default: false })),
36284
38272
  metrics: () => [
36285
- { id: "samples", name: text.metrics.samples, read: () => [...placed.values()].filter((entry) => entry.root.parent).length },
36286
- { id: "draw_calls", name: text.metrics.draw_calls, read: () => count().drawCalls },
36287
- { id: "triangles", name: text.metrics.triangles, read: () => count().triangles },
36288
- { id: "real_lights", name: text.metrics.real_lights, read: () => count().lights },
36289
- { id: "instances", name: text.metrics.instances, read: () => count().instances },
36290
- { id: "bench_objects", name: text.metrics.bench_objects, read: () => bench ? countObjects(bench.root) : 0 },
36291
- { id: "light_budget_left", name: text.metrics.light_budget_left, read: () => bench ? lightsLeft : settings.budget }
38273
+ { id: "samples", name: text2.metrics.samples, read: () => [...placed.values()].filter((entry) => entry.root.parent).length },
38274
+ { id: "draw_calls", name: text2.metrics.draw_calls, read: () => count().drawCalls },
38275
+ { id: "triangles", name: text2.metrics.triangles, read: () => count().triangles },
38276
+ { id: "real_lights", name: text2.metrics.real_lights, read: () => count().lights },
38277
+ { id: "instances", name: text2.metrics.instances, read: () => count().instances },
38278
+ { id: "bench_objects", name: text2.metrics.bench_objects, read: () => bench ? countObjects(bench.root) : 0 },
38279
+ { id: "light_budget_left", name: text2.metrics.light_budget_left, read: () => bench ? lightsLeft : settings.budget }
36292
38280
  ],
36293
38281
  attach(lent) {
36294
38282
  host = lent;