@pascal-app/viewer 1.0.3 → 1.0.4

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.
Files changed (164) hide show
  1. package/dist/components/renderers/node-renderer.d.ts.map +1 -1
  2. package/dist/components/renderers/node-renderer.js +8 -3
  3. package/dist/components/viewer/frame-limiter.d.ts +1 -0
  4. package/dist/components/viewer/frame-limiter.d.ts.map +1 -1
  5. package/dist/components/viewer/frame-limiter.js +22 -3
  6. package/dist/components/viewer/glb-interactive.d.ts +25 -1
  7. package/dist/components/viewer/glb-interactive.d.ts.map +1 -1
  8. package/dist/components/viewer/glb-interactive.js +252 -85
  9. package/dist/components/viewer/glb-mechanisms.d.ts +10 -0
  10. package/dist/components/viewer/glb-mechanisms.d.ts.map +1 -0
  11. package/dist/components/viewer/glb-mechanisms.js +19 -0
  12. package/dist/components/viewer/glb-scene.d.ts +45 -27
  13. package/dist/components/viewer/glb-scene.d.ts.map +1 -1
  14. package/dist/components/viewer/glb-scene.js +420 -506
  15. package/dist/components/viewer/glb-walkthrough-controller.d.ts +1 -1
  16. package/dist/components/viewer/glb-walkthrough-controller.d.ts.map +1 -1
  17. package/dist/components/viewer/glb-walkthrough-controller.js +12 -1
  18. package/dist/components/viewer/index.d.ts +14 -0
  19. package/dist/components/viewer/index.d.ts.map +1 -1
  20. package/dist/components/viewer/index.js +84 -26
  21. package/dist/components/viewer/lights.d.ts.map +1 -1
  22. package/dist/components/viewer/lights.js +6 -1
  23. package/dist/components/viewer/perf-monitor.d.ts.map +1 -1
  24. package/dist/components/viewer/perf-monitor.js +6 -0
  25. package/dist/components/viewer/post-processing.d.ts.map +1 -1
  26. package/dist/components/viewer/post-processing.js +8 -3
  27. package/dist/components/viewer/registered-systems.d.ts.map +1 -1
  28. package/dist/components/viewer/registered-systems.js +16 -2
  29. package/dist/components/viewer/render-error.d.ts +18 -0
  30. package/dist/components/viewer/render-error.d.ts.map +1 -0
  31. package/dist/components/viewer/render-error.js +29 -0
  32. package/dist/components/viewer/scene-ground-replacement.d.ts +6 -0
  33. package/dist/components/viewer/scene-ground-replacement.d.ts.map +1 -1
  34. package/dist/components/viewer/scene-ground-replacement.js +15 -0
  35. package/dist/components/viewer/selection-manager.d.ts.map +1 -1
  36. package/dist/components/viewer/selection-manager.js +10 -0
  37. package/dist/components/viewer/viewer-camera.d.ts +4 -0
  38. package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
  39. package/dist/components/viewer/viewer-camera.js +11 -1
  40. package/dist/index.d.ts +22 -11
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +20 -14
  43. package/dist/lib/asset-url.d.ts.map +1 -1
  44. package/dist/lib/asset-url.js +7 -1
  45. package/dist/lib/display-state.d.ts +25 -0
  46. package/dist/lib/display-state.d.ts.map +1 -0
  47. package/dist/lib/display-state.js +1 -0
  48. package/dist/lib/hero-pose.d.ts +12 -2
  49. package/dist/lib/hero-pose.d.ts.map +1 -1
  50. package/dist/lib/hero-pose.js +72 -5
  51. package/dist/lib/ink-edges.d.ts +4 -2
  52. package/dist/lib/ink-edges.d.ts.map +1 -1
  53. package/dist/lib/ink-edges.js +17 -7
  54. package/dist/lib/key-light-override.d.ts +11 -0
  55. package/dist/lib/key-light-override.d.ts.map +1 -0
  56. package/dist/lib/key-light-override.js +14 -0
  57. package/dist/lib/live-frame-hold.d.ts +14 -0
  58. package/dist/lib/live-frame-hold.d.ts.map +1 -0
  59. package/dist/lib/live-frame-hold.js +28 -0
  60. package/dist/lib/materials.d.ts +14 -7
  61. package/dist/lib/materials.d.ts.map +1 -1
  62. package/dist/lib/materials.js +31 -5
  63. package/dist/lib/node-top-surface-height.d.ts +6 -0
  64. package/dist/lib/node-top-surface-height.d.ts.map +1 -0
  65. package/dist/lib/node-top-surface-height.js +74 -0
  66. package/dist/lib/perf-probe-state.d.ts +39 -0
  67. package/dist/lib/perf-probe-state.d.ts.map +1 -0
  68. package/dist/lib/perf-probe-state.js +17 -0
  69. package/dist/lib/pointer-events.d.ts +8 -0
  70. package/dist/lib/pointer-events.d.ts.map +1 -1
  71. package/dist/lib/pointer-events.js +27 -4
  72. package/dist/lib/procedural-emission.d.ts +7 -0
  73. package/dist/lib/procedural-emission.d.ts.map +1 -0
  74. package/dist/lib/procedural-emission.js +54 -0
  75. package/dist/lib/scene-support-height.d.ts +4 -0
  76. package/dist/lib/scene-support-height.d.ts.map +1 -0
  77. package/dist/lib/scene-support-height.js +34 -0
  78. package/dist/lib/scene-visibility.d.ts +6 -0
  79. package/dist/lib/scene-visibility.d.ts.map +1 -1
  80. package/dist/lib/scene-visibility.js +15 -3
  81. package/dist/lib/scripted-opening.d.ts +11 -0
  82. package/dist/lib/scripted-opening.d.ts.map +1 -0
  83. package/dist/lib/scripted-opening.js +20 -0
  84. package/dist/lib/snapshot-pipeline.d.ts +32 -2
  85. package/dist/lib/snapshot-pipeline.d.ts.map +1 -1
  86. package/dist/lib/snapshot-pipeline.js +215 -105
  87. package/dist/lib/terrain-perimeter-fill.d.ts.map +1 -1
  88. package/dist/lib/terrain-perimeter-fill.js +22 -14
  89. package/dist/lib/zone-geometry.d.ts +11 -0
  90. package/dist/lib/zone-geometry.d.ts.map +1 -0
  91. package/dist/lib/zone-geometry.js +46 -0
  92. package/dist/store/use-item-light-pool.d.ts +14 -10
  93. package/dist/store/use-item-light-pool.d.ts.map +1 -1
  94. package/dist/store/use-item-light-pool.js +52 -26
  95. package/dist/store/use-viewer.d.ts +11 -0
  96. package/dist/store/use-viewer.d.ts.map +1 -1
  97. package/dist/store/use-viewer.js +13 -6
  98. package/dist/systems/ceiling/ceiling-system.d.ts +15 -1
  99. package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
  100. package/dist/systems/ceiling/ceiling-system.js +86 -6
  101. package/dist/systems/door/door-system.d.ts.map +1 -1
  102. package/dist/systems/door/door-system.js +55 -17
  103. package/dist/systems/floor-elevation/floor-elevation-system.d.ts +3 -1
  104. package/dist/systems/floor-elevation/floor-elevation-system.d.ts.map +1 -1
  105. package/dist/systems/floor-elevation/floor-elevation-system.js +33 -5
  106. package/dist/systems/geometry/geometry-system.d.ts.map +1 -1
  107. package/dist/systems/geometry/geometry-system.js +71 -11
  108. package/dist/systems/interactive/interactive-system.d.ts.map +1 -1
  109. package/dist/systems/interactive/interactive-system.js +40 -15
  110. package/dist/systems/interactive/procedural-controls.d.ts +13 -0
  111. package/dist/systems/interactive/procedural-controls.d.ts.map +1 -0
  112. package/dist/systems/interactive/procedural-controls.js +17 -0
  113. package/dist/systems/interactive/scripted-clips.d.ts +19 -0
  114. package/dist/systems/interactive/scripted-clips.d.ts.map +1 -0
  115. package/dist/systems/interactive/scripted-clips.js +134 -0
  116. package/dist/systems/item/item-system.d.ts.map +1 -1
  117. package/dist/systems/item/item-system.js +9 -3
  118. package/dist/systems/item/object-cut-invalidation.d.ts +3 -0
  119. package/dist/systems/item/object-cut-invalidation.d.ts.map +1 -0
  120. package/dist/systems/item/object-cut-invalidation.js +68 -0
  121. package/dist/systems/item-light/item-light-system.d.ts +1 -1
  122. package/dist/systems/item-light/item-light-system.d.ts.map +1 -1
  123. package/dist/systems/item-light/item-light-system.js +47 -52
  124. package/dist/systems/level/level-system.d.ts.map +1 -1
  125. package/dist/systems/level/level-system.js +3 -5
  126. package/dist/systems/level/level-utils.d.ts +7 -4
  127. package/dist/systems/level/level-utils.d.ts.map +1 -1
  128. package/dist/systems/level/level-utils.js +17 -6
  129. package/dist/systems/level/shadow-only-levels.d.ts +9 -0
  130. package/dist/systems/level/shadow-only-levels.d.ts.map +1 -0
  131. package/dist/systems/level/shadow-only-levels.js +7 -0
  132. package/dist/systems/roof/roof-materials.d.ts +14 -2
  133. package/dist/systems/roof/roof-materials.d.ts.map +1 -1
  134. package/dist/systems/roof/roof-materials.js +54 -15
  135. package/dist/systems/roof/roof-system.d.ts +13 -3
  136. package/dist/systems/roof/roof-system.d.ts.map +1 -1
  137. package/dist/systems/roof/roof-system.js +188 -13
  138. package/dist/systems/slab/slab-system.d.ts +1 -1
  139. package/dist/systems/slab/slab-system.d.ts.map +1 -1
  140. package/dist/systems/slab/slab-system.js +49 -19
  141. package/dist/systems/stair/stair-system.d.ts.map +1 -1
  142. package/dist/systems/stair/stair-system.js +133 -354
  143. package/dist/systems/wall/wall-cutout-cache.d.ts +7 -0
  144. package/dist/systems/wall/wall-cutout-cache.d.ts.map +1 -1
  145. package/dist/systems/wall/wall-cutout-cache.js +46 -17
  146. package/dist/systems/wall/wall-cutout.d.ts +6 -0
  147. package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
  148. package/dist/systems/wall/wall-cutout.js +42 -28
  149. package/dist/systems/wall/wall-finish-data.d.ts +22 -0
  150. package/dist/systems/wall/wall-finish-data.d.ts.map +1 -0
  151. package/dist/systems/wall/wall-finish-data.js +17 -0
  152. package/dist/systems/wall/wall-materials.d.ts +9 -4
  153. package/dist/systems/wall/wall-materials.d.ts.map +1 -1
  154. package/dist/systems/wall/wall-materials.js +99 -152
  155. package/dist/systems/wall/wall-system.d.ts +28 -2
  156. package/dist/systems/wall/wall-system.d.ts.map +1 -1
  157. package/dist/systems/wall/wall-system.js +582 -143
  158. package/dist/systems/window/window-system.d.ts +1 -1
  159. package/dist/systems/window/window-system.d.ts.map +1 -1
  160. package/dist/systems/window/window-system.js +41 -22
  161. package/package.json +5 -5
  162. package/dist/systems/fence/fence-system.d.ts +0 -22
  163. package/dist/systems/fence/fence-system.d.ts.map +0 -1
  164. package/dist/systems/fence/fence-system.js +0 -357
@@ -1,4 +1,4 @@
1
- import { createStairFlightFromStair, getEffectiveNode, getFloorStackedPosition, sceneRegistry, useScene, } from '@pascal-app/core';
1
+ import { computeSegmentTransforms, createStairFlightFromStair, getEffectiveNode, getFloorStackedPosition, measureStairDetail, resolveStraightStairConstruction, resolveWinderStairConstruction, sceneRegistry, stairSegmentConstructionError, stairSegmentDetailError, useScene, } from '@pascal-app/core';
2
2
  import { useFrame } from '@react-three/fiber';
3
3
  import * as THREE from 'three';
4
4
  import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
@@ -115,6 +115,19 @@ export const StairSystem = () => {
115
115
  }, 5);
116
116
  return null;
117
117
  };
118
+ function mergeConstructionGeometries(geometries) {
119
+ const result = mergeGeometries(geometries, true) ?? createEmptyGeometry();
120
+ result.clearGroups();
121
+ let offset = 0;
122
+ for (const geometry of geometries) {
123
+ for (const group of geometry.groups)
124
+ result.addGroup(offset + group.start, group.count, group.materialIndex);
125
+ offset += geometry.getAttribute('position').count;
126
+ geometry.dispose();
127
+ }
128
+ result.userData.stairConstructionGroups = true;
129
+ return result;
130
+ }
118
131
  // ============================================================================
119
132
  // SEGMENT GEOMETRY
120
133
  // ============================================================================
@@ -122,7 +135,91 @@ export const StairSystem = () => {
122
135
  * Generates the step/landing profile as a THREE.Shape (in the XY plane),
123
136
  * then extrudes along Z for the segment width.
124
137
  */
125
- function generateStairSegmentGeometry(segment, absoluteHeight) {
138
+ function generateStairSegmentGeometry(segment, absoluteHeight, parent) {
139
+ if (stairSegmentDetailError(segment) || stairSegmentConstructionError(segment, parent))
140
+ return createEmptyGeometry();
141
+ const winderPieces = resolveWinderStairConstruction(segment, absoluteHeight, parent);
142
+ if (winderPieces) {
143
+ const geometries = winderPieces.map((piece) => {
144
+ const polygon = piece.polygon;
145
+ const faces = THREE.ShapeUtils.triangulateShape(polygon.map(([x, z]) => new THREE.Vector2(x, z)), []);
146
+ const positions = [], materials = [];
147
+ const vertex = (i, top) => [
148
+ polygon[i][0],
149
+ top ? piece.top : piece.bottom[i],
150
+ polygon[i][1],
151
+ ];
152
+ const triangle = (a, b, c, material) => {
153
+ positions.push(...a, ...b, ...c);
154
+ materials.push(material);
155
+ };
156
+ for (const face of faces) {
157
+ let [a, b, c] = face;
158
+ const pa = polygon[a], pb = polygon[b], pc = polygon[c];
159
+ if ((pb[0] - pa[0]) * (pc[1] - pa[1]) - (pb[1] - pa[1]) * (pc[0] - pa[0]) < 0)
160
+ [b, c] = [c, b];
161
+ triangle(vertex(a, true), vertex(c, true), vertex(b, true), piece.role === 'tread' || piece.walkingTop ? 0 : 1);
162
+ triangle(vertex(a, false), vertex(b, false), vertex(c, false), piece.role === 'tread' ? 0 : 1);
163
+ }
164
+ const signedArea = polygon.reduce((sum, a, i) => {
165
+ const b = polygon[(i + 1) % polygon.length];
166
+ return sum + a[0] * b[1] - a[1] * b[0];
167
+ }, 0);
168
+ const ring = signedArea > 0 ? polygon.map((_, i) => i) : polygon.map((_, i) => polygon.length - 1 - i);
169
+ for (let k = 0; k < ring.length; k++) {
170
+ const i = ring[k], j = ring[(k + 1) % ring.length];
171
+ triangle(vertex(i, true), vertex(j, true), vertex(j, false), piece.role === 'tread' ? 0 : 1);
172
+ triangle(vertex(i, true), vertex(j, false), vertex(i, false), piece.role === 'tread' ? 0 : 1);
173
+ }
174
+ const geometry = new THREE.BufferGeometry();
175
+ geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
176
+ geometry.computeVertexNormals();
177
+ const uvs = [];
178
+ const a = new THREE.Vector3(), b = new THREE.Vector3(), c = new THREE.Vector3();
179
+ const u = new THREE.Vector3(), normal = new THREE.Vector3(), v = new THREE.Vector3();
180
+ for (let i = 0; i < positions.length; i += 9) {
181
+ a.fromArray(positions, i);
182
+ b.fromArray(positions, i + 3);
183
+ c.fromArray(positions, i + 6);
184
+ u.subVectors(b, a).normalize();
185
+ normal.subVectors(b, a).cross(v.subVectors(c, a)).normalize();
186
+ v.crossVectors(normal, u);
187
+ uvs.push(a.dot(u), a.dot(v), b.dot(u), b.dot(v), c.dot(u), c.dot(v));
188
+ }
189
+ geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2));
190
+ ensureUv2Attribute(geometry);
191
+ materials.forEach((material, i) => {
192
+ geometry.addGroup(i * 3, 3, material);
193
+ });
194
+ return geometry;
195
+ });
196
+ return mergeConstructionGeometries(geometries);
197
+ }
198
+ const pieces = resolveStraightStairConstruction(segment, absoluteHeight, parent);
199
+ if (pieces) {
200
+ const geometries = pieces.map((piece) => {
201
+ const shape = new THREE.Shape(piece.profile.map(([z, y]) => new THREE.Vector2(z, y)));
202
+ const geometry = new THREE.ExtrudeGeometry(shape, {
203
+ steps: 1,
204
+ depth: piece.x1 - piece.x0,
205
+ bevelEnabled: false,
206
+ });
207
+ const matrix = new THREE.Matrix4().makeRotationY(-Math.PI / 2);
208
+ matrix.setPosition(piece.x1, 0, 0);
209
+ geometry.applyMatrix4(matrix);
210
+ geometry.computeVertexNormals();
211
+ applyStairSegmentUvs(geometry);
212
+ ensureUv2Attribute(geometry);
213
+ if (piece.role === 'tread' || !piece.walkingTop) {
214
+ geometry.clearGroups();
215
+ geometry.addGroup(0, geometry.getAttribute('position').count, piece.role === 'tread' ? STAIR_TREAD_MATERIAL_INDEX : STAIR_SIDE_MATERIAL_INDEX);
216
+ }
217
+ else
218
+ applyStraightStairMaterialGroups(geometry);
219
+ return geometry;
220
+ });
221
+ return mergeConstructionGeometries(geometries);
222
+ }
126
223
  const { width, length, height, stepCount, segmentType, fillToFloor, thickness } = segment;
127
224
  const shape = new THREE.Shape();
128
225
  if (segmentType === 'landing') {
@@ -194,7 +291,9 @@ function generateStairSegmentGeometry(segment, absoluteHeight) {
194
291
  function updateStairSegmentGeometry(node, mesh) {
195
292
  // Compute absolute height from parent chain
196
293
  const absoluteHeight = computeAbsoluteHeight(node);
197
- const newGeometry = generateStairSegmentGeometry(node, absoluteHeight);
294
+ const parent = node.parentId ? useScene.getState().nodes[node.parentId] : undefined;
295
+ const effectiveParent = parent?.type === 'stair' ? getEffectiveNode(parent) : undefined;
296
+ const newGeometry = generateStairSegmentGeometry(node, absoluteHeight, effectiveParent);
198
297
  applyStraightStairMaterialGroups(newGeometry);
199
298
  mesh.geometry.dispose();
200
299
  mesh.geometry = newGeometry;
@@ -223,6 +322,8 @@ function syncSegmentMeshTransforms(stairNode, nodes) {
223
322
  const transform = transforms[i];
224
323
  const mesh = sceneRegistry.nodes.get(segment.id);
225
324
  if (mesh) {
325
+ if (mesh.position.y !== transform.position[1])
326
+ useScene.getState().markDirty(segment.id);
226
327
  mesh.position.set(transform.position[0], transform.position[1], transform.position[2]);
227
328
  mesh.rotation.y = transform.rotation;
228
329
  }
@@ -274,14 +375,23 @@ function updateMergedStairGeometry(stairNode, group, nodes) {
274
375
  // it is switched to straight. Draw the flight its own fields describe
275
376
  // instead of vanishing; it is the same flight the panel materializes.
276
377
  const bodySegments = segments.length > 0 ? segments : [createStairFlightFromStair(stairNode, nodes)];
378
+ if (measureStairDetail(stairNode, bodySegments).error) {
379
+ replaceMeshGeometry(mergedMesh, createEmptyGeometry());
380
+ return;
381
+ }
277
382
  // Compute chained transforms for segments
278
383
  const transforms = computeSegmentTransforms(bodySegments);
279
384
  const geometries = [];
385
+ const materialOffsets = [];
280
386
  for (let i = 0; i < bodySegments.length; i++) {
281
387
  const segment = bodySegments[i];
388
+ if (segment.visible === false)
389
+ continue;
390
+ materialOffsets.push(i * 2);
282
391
  const transform = transforms[i];
283
392
  const absoluteHeight = transform.position[1];
284
- const geo = generateStairSegmentGeometry(segment, absoluteHeight);
393
+ const geo = generateStairSegmentGeometry(segment, absoluteHeight, stairNode);
394
+ applyStraightStairMaterialGroups(geo);
285
395
  // Apply segment transform (position + rotation) relative to parent stair
286
396
  _position.set(transform.position[0], transform.position[1], transform.position[2]);
287
397
  _quaternion.setFromAxisAngle(_yAxis, transform.rotation);
@@ -289,8 +399,17 @@ function updateMergedStairGeometry(stairNode, group, nodes) {
289
399
  geo.applyMatrix4(_matrix);
290
400
  geometries.push(geo);
291
401
  }
292
- const merged = mergeGeometries(geometries, false) ?? createEmptyGeometry();
293
- applyStraightStairMaterialGroups(merged);
402
+ const merged = (geometries.length ? mergeGeometries(geometries, false) : null) ?? createEmptyGeometry();
403
+ merged.clearGroups();
404
+ let vertexOffset = 0;
405
+ for (const [index, geometry] of geometries.entries()) {
406
+ for (const group of geometry.groups)
407
+ merged.addGroup(vertexOffset + group.start, group.count, materialOffsets[index] + (group.materialIndex ?? 0));
408
+ vertexOffset += geometry.getAttribute('position').count;
409
+ }
410
+ mergedMesh.userData.slotIds = bodySegments.flatMap(() => ['treads', 'body']);
411
+ mergedMesh.userData.surfaceNodeIds = bodySegments.flatMap((segment) => [segment.id, segment.id]);
412
+ mergedMesh.userData.segmentIds = bodySegments.flatMap((segment) => [segment.id, segment.id]);
294
413
  replaceMeshGeometry(mergedMesh, merged);
295
414
  // Dispose individual geometries
296
415
  for (const geo of geometries) {
@@ -298,6 +417,8 @@ function updateMergedStairGeometry(stairNode, group, nodes) {
298
417
  }
299
418
  }
300
419
  function applyStraightStairMaterialGroups(geometry) {
420
+ if (geometry.userData.stairConstructionGroups)
421
+ return;
301
422
  const position = geometry.getAttribute('position');
302
423
  if (!position || position.count < 3) {
303
424
  geometry.clearGroups();
@@ -377,57 +498,9 @@ function ensureUv2Attribute(geometry) {
377
498
  return;
378
499
  geometry.setAttribute('uv2', new THREE.Float32BufferAttribute(Array.from(uv.array), 2));
379
500
  }
380
- /**
381
- * Computes world-relative transforms for each segment by chaining
382
- * based on attachmentSide. This mirrors the prototype's StairSystem logic.
383
- */
384
- function computeSegmentTransforms(segments) {
385
- const transforms = [];
386
- let currentPos = new THREE.Vector3(0, 0, 0);
387
- let currentRot = 0;
388
- for (let i = 0; i < segments.length; i++) {
389
- const segment = segments[i];
390
- if (i === 0) {
391
- transforms.push({
392
- position: [currentPos.x, currentPos.y, currentPos.z],
393
- rotation: currentRot,
394
- });
395
- }
396
- else {
397
- const prev = segments[i - 1];
398
- const localAttachPos = new THREE.Vector3();
399
- let rotChange = 0;
400
- switch (segment.attachmentSide) {
401
- case 'front':
402
- localAttachPos.set(0, prev.height, prev.length);
403
- rotChange = 0;
404
- break;
405
- case 'left':
406
- localAttachPos.set(prev.width / 2, prev.height, prev.length / 2);
407
- rotChange = Math.PI / 2;
408
- break;
409
- case 'right':
410
- localAttachPos.set(-prev.width / 2, prev.height, prev.length / 2);
411
- rotChange = -Math.PI / 2;
412
- break;
413
- }
414
- // Rotate local attachment point by previous global rotation
415
- localAttachPos.applyAxisAngle(new THREE.Vector3(0, 1, 0), currentRot);
416
- currentPos = currentPos.clone().add(localAttachPos);
417
- currentRot += rotChange;
418
- transforms.push({
419
- position: [currentPos.x, currentPos.y, currentPos.z],
420
- rotation: currentRot,
421
- });
422
- }
423
- }
424
- return transforms;
425
- }
426
- function rotateXZ(x, z, angle) {
427
- const cos = Math.cos(angle);
428
- const sin = Math.sin(angle);
429
- return [x * cos + z * sin, -x * sin + z * cos];
430
- }
501
+ // ============================================================================
502
+ // SEGMENT CHAINING
503
+ // ============================================================================
431
504
  function createEmptyGeometry() {
432
505
  const geometry = new THREE.BufferGeometry();
433
506
  // Three zero-vertices (one degenerate, invisible triangle), not an empty
@@ -446,301 +519,6 @@ function replaceMeshGeometry(mesh, geometry) {
446
519
  mesh.geometry.dispose();
447
520
  mesh.geometry = geometry;
448
521
  }
449
- function generateStairRailingGeometry(stairNode, segments, transforms) {
450
- const railingMode = stairNode.railingMode ?? 'none';
451
- if (railingMode === 'none') {
452
- return createEmptyGeometry();
453
- }
454
- const railHeight = Math.max(0.5, stairNode.railingHeight ?? 0.92);
455
- const midRailHeight = Math.max(railHeight * 0.45, 0.35);
456
- const railRadius = 0.022;
457
- const postRadius = 0.018;
458
- const inset = 0.06;
459
- const landingInset = 0.08;
460
- const geometries = [];
461
- const segmentRailPaths = buildStairRailPaths(segments, transforms, railingMode, inset, landingInset);
462
- for (const segmentRailPath of segmentRailPaths) {
463
- for (const sidePath of segmentRailPath.sidePaths) {
464
- const points = sidePath.points;
465
- if (points.length === 0)
466
- continue;
467
- geometries.push(...buildBalusterGeometries(points, railHeight, postRadius));
468
- geometries.push(...buildOffsetRailSegmentGeometries(points, railHeight, railRadius));
469
- geometries.push(...buildOffsetRailSegmentGeometries(points, midRailHeight, railRadius * 0.8));
470
- }
471
- }
472
- for (let index = 1; index < segmentRailPaths.length; index++) {
473
- const previousPath = segmentRailPaths[index - 1];
474
- const currentPath = segmentRailPaths[index];
475
- if (!(previousPath && currentPath?.connectFromPrevious))
476
- continue;
477
- if (previousPath.segment.segmentType === 'landing')
478
- continue;
479
- for (const sidePath of currentPath.sidePaths) {
480
- if (currentPath.segment.segmentType === 'landing')
481
- continue;
482
- const currentPoint = sidePath.points[0];
483
- if (!currentPoint)
484
- continue;
485
- const previousSidePath = [...previousPath.sidePaths]
486
- .map((entry) => ({
487
- entry,
488
- distance: entry.points.length
489
- ? entry.points[entry.points.length - 1].distanceTo(currentPoint)
490
- : Number.POSITIVE_INFINITY,
491
- }))
492
- .sort((left, right) => left.distance - right.distance)[0]?.entry;
493
- const previousPoint = previousSidePath && previousSidePath.points.length > 0
494
- ? previousSidePath.points[previousSidePath.points.length - 1]
495
- : null;
496
- if (!(previousPoint && currentPoint))
497
- continue;
498
- const connectorPoints = [previousPoint, currentPoint];
499
- geometries.push(...buildOffsetRailSegmentGeometries(connectorPoints, railHeight, railRadius));
500
- geometries.push(...buildOffsetRailSegmentGeometries(connectorPoints, midRailHeight, railRadius * 0.8));
501
- }
502
- }
503
- const merged = mergeGeometries(geometries, false) ?? createEmptyGeometry();
504
- for (const geometry of geometries) {
505
- geometry.dispose();
506
- }
507
- return merged;
508
- }
509
- function buildStairRailPaths(segments, transforms, railingMode, inset, landingInset) {
510
- const layouts = computeStairRailLayouts(segments, transforms);
511
- if (railingMode === 'both') {
512
- const isStraightLineDoubleLandingLayout = segments.length === 4 &&
513
- segments[0]?.segmentType === 'stair' &&
514
- segments[1]?.segmentType === 'landing' &&
515
- segments[2]?.segmentType === 'stair' &&
516
- segments[2]?.attachmentSide === 'front' &&
517
- segments[3]?.segmentType === 'landing' &&
518
- segments[3]?.attachmentSide === 'front';
519
- return layouts.map((layout, index) => {
520
- const segment = layout.segment;
521
- const previousSegment = index > 0 ? segments[index - 1] : undefined;
522
- const nextSegment = index < segments.length - 1 ? segments[index + 1] : undefined;
523
- const hideLandingRailing = segment.segmentType === 'landing' &&
524
- previousSegment?.segmentType === 'stair' &&
525
- nextSegment?.segmentType === 'stair';
526
- const visualTurnSide = nextSegment?.attachmentSide;
527
- const sideCandidates = hideLandingRailing
528
- ? visualTurnSide === 'left'
529
- ? ['front', 'right']
530
- : visualTurnSide === 'right'
531
- ? ['front', 'left']
532
- : ['left', 'right']
533
- : segment.segmentType === 'landing'
534
- ? nextSegment?.segmentType === 'landing' && visualTurnSide === 'left'
535
- ? ['front', 'right']
536
- : nextSegment?.segmentType === 'landing' && visualTurnSide === 'right'
537
- ? ['front', 'left']
538
- : visualTurnSide === 'left'
539
- ? ['right']
540
- : visualTurnSide === 'right'
541
- ? ['left']
542
- : ['left', 'right']
543
- : ['left', 'right'];
544
- const sidePaths = sideCandidates
545
- .map((side) => buildSegmentRailPath(layout, side, previousSegment, nextSegment, inset, landingInset))
546
- .filter((entry) => entry !== null);
547
- return {
548
- segment,
549
- sidePaths: isStraightLineDoubleLandingLayout && index === 1
550
- ? ['left', 'right']
551
- .map((side) => buildSegmentRailPath(layout, side, previousSegment, nextSegment, inset, landingInset))
552
- .filter((entry) => entry !== null)
553
- : sidePaths,
554
- connectFromPrevious: index > 0 &&
555
- !(previousSegment?.segmentType === 'landing' && segment.segmentType === 'landing'),
556
- };
557
- });
558
- }
559
- const isStraightLineDoubleLandingLayout = segments.length === 4 &&
560
- segments[0]?.segmentType === 'stair' &&
561
- segments[1]?.segmentType === 'landing' &&
562
- segments[2]?.segmentType === 'stair' &&
563
- segments[2]?.attachmentSide === 'front' &&
564
- segments[3]?.segmentType === 'landing' &&
565
- segments[3]?.attachmentSide === 'front';
566
- const resolved = [];
567
- layouts.forEach((layout, index) => {
568
- const segment = layout.segment;
569
- const previousSegment = index > 0 ? segments[index - 1] : undefined;
570
- const nextSegment = index < segments.length - 1 ? segments[index + 1] : undefined;
571
- const nextAttachmentSide = nextSegment?.attachmentSide;
572
- const isMiddleLandingBetweenFlights = segment.segmentType === 'landing' &&
573
- previousSegment?.segmentType === 'stair' &&
574
- nextSegment?.segmentType === 'stair';
575
- const suppressLandingRailing = segment.segmentType === 'landing' &&
576
- nextSegment?.segmentType === 'landing' &&
577
- nextAttachmentSide === railingMode;
578
- const landingContinuesOnPreferredSide = segment.segmentType === 'landing'
579
- ? nextAttachmentSide == null ||
580
- nextAttachmentSide === 'front' ||
581
- nextAttachmentSide === railingMode
582
- : true;
583
- const sidePaths = suppressLandingRailing
584
- ? []
585
- : segment.segmentType !== 'landing'
586
- ? [
587
- buildSegmentRailPath(layout, railingMode, previousSegment, nextSegment, inset, landingInset),
588
- ]
589
- : isStraightLineDoubleLandingLayout
590
- ? [
591
- buildSegmentRailPath(layout, railingMode, previousSegment, nextSegment, inset, landingInset),
592
- ]
593
- : isMiddleLandingBetweenFlights && railingMode === 'left'
594
- ? nextAttachmentSide === 'right'
595
- ? [
596
- buildSegmentRailPath(layout, 'front', previousSegment, nextSegment, inset, landingInset),
597
- buildSegmentRailPath(layout, 'left', previousSegment, nextSegment, inset, landingInset),
598
- ]
599
- : []
600
- : isMiddleLandingBetweenFlights && railingMode === 'right'
601
- ? nextAttachmentSide === 'left'
602
- ? [
603
- buildSegmentRailPath(layout, 'front', previousSegment, nextSegment, inset, landingInset),
604
- buildSegmentRailPath(layout, 'right', previousSegment, nextSegment, inset, landingInset),
605
- ]
606
- : []
607
- : nextSegment?.segmentType === 'landing' &&
608
- nextAttachmentSide != null &&
609
- nextAttachmentSide !== 'front' &&
610
- nextAttachmentSide !== railingMode
611
- ? [
612
- buildSegmentRailPath(layout, 'front', previousSegment, nextSegment, inset, landingInset),
613
- buildSegmentRailPath(layout, railingMode, previousSegment, nextSegment, inset, landingInset),
614
- ]
615
- : [
616
- buildSegmentRailPath(layout, railingMode, previousSegment, nextSegment, inset, landingInset),
617
- ];
618
- resolved.push({
619
- segment,
620
- sidePaths: sidePaths.filter((entry) => entry !== null),
621
- connectFromPrevious: index > 0 &&
622
- !suppressLandingRailing &&
623
- sidePaths.length > 0 &&
624
- (segment.segmentType === 'landing' ? landingContinuesOnPreferredSide : true),
625
- });
626
- });
627
- return resolved;
628
- }
629
- function computeStairRailLayouts(segments, transforms) {
630
- return segments.map((segment, index) => {
631
- const transform = transforms[index];
632
- const [centerOffsetX, centerOffsetZ] = rotateXZ(0, segment.length / 2, transform.rotation);
633
- return {
634
- center: [transform.position[0] + centerOffsetX, transform.position[2] + centerOffsetZ],
635
- elevation: transform.position[1],
636
- rotation: transform.rotation,
637
- segment,
638
- };
639
- });
640
- }
641
- function buildSegmentRailPath(layout, side, previousSegment, nextSegment, inset, landingInset) {
642
- const segment = layout.segment;
643
- const segmentSteps = Math.max(1, segment.segmentType === 'landing' ? 1 : segment.stepCount);
644
- const segmentStepDepth = segment.length / segmentSteps;
645
- const segmentStepHeight = segment.segmentType === 'landing' ? 0 : segment.height / segmentSteps;
646
- const segmentTopThickness = getSegmentTopThickness(segment);
647
- const flightSideOffset = side === 'left' ? segment.width / 2 - 0.045 : -segment.width / 2 + 0.045;
648
- const flightStartX = previousSegment?.segmentType === 'landing'
649
- ? -segment.length / 2 + landingInset
650
- : -segment.length / 2;
651
- const flightEndX = nextSegment?.segmentType === 'landing' ? segment.length / 2 - landingInset : segment.length / 2;
652
- if (segment.segmentType === 'landing') {
653
- return buildLandingRailPathFromScratch(layout, side, previousSegment, nextSegment, segmentTopThickness, landingInset);
654
- }
655
- return {
656
- side,
657
- points: [
658
- ...(previousSegment?.segmentType === 'landing'
659
- ? []
660
- : [toRailLayoutWorldPoint(layout, flightStartX, segmentTopThickness, flightSideOffset)]),
661
- ...Array.from({ length: segmentSteps }).map((_, index) => toRailLayoutWorldPoint(layout, -segment.length / 2 + segmentStepDepth * index + segmentStepDepth / 2, segmentStepHeight * (index + 1), flightSideOffset)),
662
- ...(nextSegment?.segmentType === 'landing'
663
- ? []
664
- : [toRailLayoutWorldPoint(layout, flightEndX, segment.height, flightSideOffset)]),
665
- ],
666
- };
667
- }
668
- function buildLandingRailPathFromScratch(layout, side, previousSegment, nextSegment, topY, inset) {
669
- const segment = layout.segment;
670
- const backX = -segment.length / 2 + inset;
671
- const frontX = segment.length / 2 - inset;
672
- const leftZ = segment.width / 2 - inset;
673
- const rightZ = -segment.width / 2 + inset;
674
- const edgePoints = side === 'left'
675
- ? [
676
- toRailLayoutWorldPoint(layout, backX, topY, leftZ),
677
- toRailLayoutWorldPoint(layout, frontX, topY, leftZ),
678
- ]
679
- : side === 'right'
680
- ? [
681
- toRailLayoutWorldPoint(layout, backX, topY, rightZ),
682
- toRailLayoutWorldPoint(layout, frontX, topY, rightZ),
683
- ]
684
- : [
685
- // When the next flight turns, rail the visible leading edge nearest the turn opening.
686
- toRailLayoutWorldPoint(layout, previousSegment?.segmentType === 'stair' &&
687
- nextSegment?.attachmentSide &&
688
- nextSegment.attachmentSide !== 'front'
689
- ? backX
690
- : frontX, topY, leftZ),
691
- toRailLayoutWorldPoint(layout, previousSegment?.segmentType === 'stair' &&
692
- nextSegment?.attachmentSide &&
693
- nextSegment.attachmentSide !== 'front'
694
- ? backX
695
- : frontX, topY, rightZ),
696
- ];
697
- return {
698
- side,
699
- points: edgePoints,
700
- };
701
- }
702
- function toRailLayoutWorldPoint(layout, localX, localY, localZ) {
703
- const [offsetX, offsetZ] = rotateXZ(localZ, localX, layout.rotation);
704
- return new THREE.Vector3(layout.center[0] + offsetX, layout.elevation + localY, layout.center[1] + offsetZ);
705
- }
706
- function buildOffsetRailSegmentGeometries(points, heightOffset, radius) {
707
- const geometries = [];
708
- for (let index = 0; index < points.length - 1; index++) {
709
- const start = points[index];
710
- const end = points[index + 1];
711
- if (!(start && end))
712
- continue;
713
- const segmentGeometry = createCylinderBetweenPoints(start.clone().add(new THREE.Vector3(0, heightOffset, 0)), end.clone().add(new THREE.Vector3(0, heightOffset, 0)), radius, 8);
714
- if (segmentGeometry) {
715
- geometries.push(segmentGeometry);
716
- }
717
- }
718
- return geometries;
719
- }
720
- function buildBalusterGeometries(points, height, radius) {
721
- const geometries = [];
722
- for (const point of points) {
723
- const geometry = new THREE.CylinderGeometry(radius, radius, Math.max(height, 0.05), 8);
724
- geometry.translate(point.x, point.y + height / 2, point.z);
725
- geometries.push(geometry);
726
- }
727
- return geometries;
728
- }
729
- function getSegmentTopThickness(segment) {
730
- return Math.max(segment.thickness ?? 0.25, 0.02);
731
- }
732
- function createCylinderBetweenPoints(start, end, radius, radialSegments) {
733
- const direction = new THREE.Vector3().subVectors(end, start);
734
- const length = direction.length();
735
- if (length <= 1e-5)
736
- return null;
737
- const midpoint = new THREE.Vector3().addVectors(start, end).multiplyScalar(0.5);
738
- const quaternion = new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0, 1, 0), direction.clone().normalize());
739
- const geometry = new THREE.CylinderGeometry(radius, radius, length, radialSegments);
740
- geometry.applyQuaternion(quaternion);
741
- geometry.translate(midpoint.x, midpoint.y, midpoint.z);
742
- return geometry;
743
- }
744
522
  /**
745
523
  * Computes the absolute Y height of a segment by traversing the stair's segment chain.
746
524
  */
@@ -751,10 +529,11 @@ function computeAbsoluteHeight(node) {
751
529
  const parent = nodes[node.parentId];
752
530
  if (parent?.type !== 'stair')
753
531
  return 0;
754
- const stair = parent;
532
+ const stair = getEffectiveNode(parent);
755
533
  const segments = (stair.children ?? [])
756
534
  .map((childId) => nodes[childId])
757
- .filter((n) => n?.type === 'stair-segment');
535
+ .filter((n) => n?.type === 'stair-segment')
536
+ .map((segment) => getEffectiveNode(segment));
758
537
  const transforms = computeSegmentTransforms(segments);
759
538
  const index = segments.findIndex((s) => s.id === node.id);
760
539
  if (index < 0)
@@ -20,6 +20,8 @@ type CachedWall = {
20
20
  assignedMaterials: Material | Material[] | undefined;
21
21
  visibleVariant: Variant;
22
22
  hiddenVariant: Variant;
23
+ /** Finish refs the material palette was built for; the geometry's groups index them. */
24
+ paletteKey: string;
23
25
  };
24
26
  export declare const WALL_FACING_HYSTERESIS = 0.001;
25
27
  export declare function wallHiddenFromFacing(node: Pick<WallNode, 'frontSide' | 'backSide'>, mode: WallMode, negativeFacing: boolean): boolean;
@@ -52,6 +54,11 @@ export declare class WallCutoutCache {
52
54
  constructor(viewerStore?: WallCutoutViewerStore, materialResolver?: WallMaterialsResolver);
53
55
  subscribeLiveTransforms(): () => void;
54
56
  update(camera: Camera, time: number): void;
57
+ /**
58
+ * A rebuilt wall whose finish palette changed must swap materials in the same
59
+ * frame: its new groups already index the new palette.
60
+ */
61
+ handleRebuilt(id: string): void;
55
62
  private refreshAppearance;
56
63
  private refreshNormal;
57
64
  private apply;
@@ -1 +1 @@
1
- {"version":3,"file":"wall-cutout-cache.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-cutout-cache.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"wall-cutout-cache.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-cutout-cache.ts"],"names":[],"mappings":"AAEA,OAAO,EAQL,KAAK,QAAQ,EACd,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,QAAQ,EAAE,OAAO,EAAE,KAAK,IAAI,EAAiB,OAAO,EAAE,MAAM,OAAO,CAAA;AAG9F,OAAO,SAAS,EAAE,EAAE,KAAK,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AAEjE,OAAO,EAA8B,KAAK,mBAAmB,EAAE,MAAM,yBAAyB,CAAA;AAC9F,OAAO,EAKL,KAAK,qBAAqB,EAC3B,MAAM,kBAAkB,CAAA;AAEzB,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,QAAQ,GAAG,QAAQ,EAAE,EAAE,CAAC,EAAE,QAAQ,EAAE,GAAG,OAAO,CAElF;AAgCD,KAAK,OAAO,GAAG;IAAE,GAAG,EAAE,mBAAmB,CAAC;IAAC,SAAS,EAAE,QAAQ,EAAE,CAAA;CAAE,CAAA;AAClE,KAAK,UAAU,GAAG;IAChB,IAAI,EAAE,IAAI,CAAA;IACV,IAAI,EAAE,QAAQ,CAAA;IACd,MAAM,EAAE,OAAO,CAAA;IACf,MAAM,EAAE,OAAO,CAAA;IACf,QAAQ,EAAE,IAAI,CAAC,UAAU,CAAC,CAAA;IAC1B,cAAc,EAAE,OAAO,GAAG,SAAS,CAAA;IACnC,MAAM,EAAE,OAAO,GAAG,SAAS,CAAA;IAC3B,UAAU,EAAE,mBAAmB,GAAG,SAAS,CAAA;IAC3C,iBAAiB,EAAE,QAAQ,GAAG,QAAQ,EAAE,GAAG,SAAS,CAAA;IACpD,cAAc,EAAE,OAAO,CAAA;IACvB,aAAa,EAAE,OAAO,CAAA;IACtB,wFAAwF;IACxF,UAAU,EAAE,MAAM,CAAA;CACnB,CAAA;AAQD,eAAO,MAAM,sBAAsB,QAAQ,CAAA;AAE3C,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,WAAW,GAAG,UAAU,CAAC,EAC9C,IAAI,EAAE,QAAQ,EACd,cAAc,EAAE,OAAO,GACtB,OAAO,CAOT;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,CAGtF;AAED,MAAM,MAAM,qBAAqB,GAAG,IAAI,CACtC,UAAU,CAAC,OAAO,SAAS,CAAC,QAAQ,CAAC,EACnC,UAAU,GACV,SAAS,GACT,UAAU,GACV,aAAa,GACb,YAAY,GACZ,WAAW,GACX,oBAAoB,GACpB,WAAW,GACX,oBAAoB,CACvB,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;CAAE,CAAA;AAE7E,qBAAa,eAAe;IAqBxB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IArBnC,QAAQ,CAAC,KAAK,0BAAgC;IAC9C,QAAQ,CAAC,OAAO,cAAoB;IACpC,OAAO,CAAC,MAAM,CAAmC;IACjD,OAAO,CAAC,KAAK,CAA2D;IACxE,OAAO,CAAC,SAAS,CAA+D;IAChF,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,kBAAkB,CAAgB;IAC1C,OAAO,CAAC,gBAAgB,CAAgB;IACxC,OAAO,CAAC,eAAe,CAAgB;IACvC,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,cAAc,CAAI;IAC1B,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,QAAQ,CAAoB;IACpC,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,WAAW,CAAoB;IACvC,OAAO,CAAC,SAAS,CAA4C;gBAG1C,WAAW,GAAE,qBAAiC,EAC9C,gBAAgB,GAAE,qBAA2C;IAGhF,uBAAuB,IAAI,MAAM,IAAI;IAWrC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IA+J1C;;;OAGG;IACH,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAa/B,OAAO,CAAC,iBAAiB;IAiCzB,OAAO,CAAC,aAAa;IAcrB,OAAO,CAAC,KAAK;CAyCd"}