@pascal-app/viewer 0.7.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/renderers/node-renderer.d.ts +1 -1
- package/dist/components/renderers/node-renderer.d.ts.map +1 -1
- package/dist/components/renderers/node-renderer.js +38 -22
- package/dist/components/renderers/parametric-node-renderer.d.ts +5 -0
- package/dist/components/renderers/parametric-node-renderer.d.ts.map +1 -0
- package/dist/components/renderers/parametric-node-renderer.js +34 -0
- package/dist/components/renderers/scene-renderer.d.ts +1 -1
- package/dist/components/renderers/scene-renderer.d.ts.map +1 -1
- package/dist/components/viewer/frame-limiter.d.ts.map +1 -1
- package/dist/components/viewer/frame-limiter.js +5 -1
- package/dist/components/viewer/ground-occluder.d.ts +1 -1
- package/dist/components/viewer/ground-occluder.d.ts.map +1 -1
- package/dist/components/viewer/ground-occluder.js +12 -24
- package/dist/components/viewer/index.d.ts +38 -1
- package/dist/components/viewer/index.d.ts.map +1 -1
- package/dist/components/viewer/index.js +183 -60
- package/dist/components/viewer/lights.d.ts +1 -1
- package/dist/components/viewer/lights.d.ts.map +1 -1
- package/dist/components/viewer/lights.js +170 -43
- package/dist/components/viewer/perf-monitor.d.ts +1 -1
- package/dist/components/viewer/perf-monitor.d.ts.map +1 -1
- package/dist/components/viewer/perf-monitor.js +99 -11
- package/dist/components/viewer/post-processing.d.ts.map +1 -1
- package/dist/components/viewer/post-processing.js +217 -61
- package/dist/components/viewer/registered-systems.d.ts +12 -0
- package/dist/components/viewer/registered-systems.d.ts.map +1 -0
- package/dist/components/viewer/registered-systems.js +47 -0
- package/dist/components/viewer/scene-bvh.d.ts +17 -0
- package/dist/components/viewer/scene-bvh.d.ts.map +1 -0
- package/dist/components/viewer/scene-bvh.js +94 -0
- package/dist/components/viewer/selection-manager.d.ts +1 -1
- package/dist/components/viewer/selection-manager.d.ts.map +1 -1
- package/dist/components/viewer/selection-manager.js +20 -5
- package/dist/components/viewer/viewer-camera.d.ts +1 -1
- package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
- package/dist/components/viewer/walkthrough-controls.d.ts +1 -1
- package/dist/components/viewer/walkthrough-controls.d.ts.map +1 -1
- package/dist/hooks/use-gltf-ktx2.d.ts.map +1 -1
- package/dist/hooks/use-node-events.d.ts +5 -73
- package/dist/hooks/use-node-events.d.ts.map +1 -1
- package/dist/hooks/use-node-events.js +19 -7
- package/dist/index.d.ts +36 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +67 -3
- package/dist/lib/csg-utils.d.ts +16 -0
- package/dist/lib/csg-utils.d.ts.map +1 -0
- package/dist/lib/csg-utils.js +33 -0
- package/dist/lib/drawable-geometry.d.ts +21 -0
- package/dist/lib/drawable-geometry.d.ts.map +1 -0
- package/dist/lib/drawable-geometry.js +22 -0
- package/dist/lib/edge-style.d.ts +3 -0
- package/dist/lib/edge-style.d.ts.map +1 -0
- package/dist/lib/edge-style.js +11 -0
- package/dist/lib/gpu-perf.d.ts +4 -0
- package/dist/lib/gpu-perf.d.ts.map +1 -0
- package/dist/lib/gpu-perf.js +23 -0
- package/dist/lib/ink-edges.d.ts +9 -0
- package/dist/lib/ink-edges.d.ts.map +1 -0
- package/dist/lib/ink-edges.js +43 -0
- package/dist/lib/isolation.d.ts +39 -0
- package/dist/lib/isolation.d.ts.map +1 -0
- package/dist/lib/isolation.js +103 -0
- package/dist/lib/layers.d.ts +18 -0
- package/dist/lib/layers.d.ts.map +1 -1
- package/dist/lib/layers.js +18 -0
- package/dist/lib/materials.d.ts +25 -15
- package/dist/lib/materials.d.ts.map +1 -1
- package/dist/lib/materials.js +277 -63
- package/dist/lib/merged-outline-node.d.ts.map +1 -1
- package/dist/lib/merged-outline-node.js +11 -11
- package/dist/lib/polygon-union.d.ts +4 -0
- package/dist/lib/polygon-union.d.ts.map +1 -0
- package/dist/lib/polygon-union.js +289 -0
- package/dist/lib/polygon-union.test.d.ts +2 -0
- package/dist/lib/polygon-union.test.d.ts.map +1 -0
- package/dist/lib/polygon-union.test.js +105 -0
- package/dist/lib/scene-themes.d.ts +29 -0
- package/dist/lib/scene-themes.d.ts.map +1 -0
- package/dist/lib/scene-themes.js +193 -0
- package/dist/store/use-viewer.d.ts +42 -4
- package/dist/store/use-viewer.d.ts.map +1 -1
- package/dist/store/use-viewer.js +26 -3
- package/dist/systems/ceiling/ceiling-system.d.ts +6 -2
- package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
- package/dist/systems/ceiling/ceiling-system.js +62 -12
- package/dist/systems/door/door-system.d.ts.map +1 -1
- package/dist/systems/door/door-system.js +373 -22
- package/dist/systems/elevator/elevator-interaction-system.d.ts +2 -0
- package/dist/systems/elevator/elevator-interaction-system.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-interaction-system.js +62 -0
- package/dist/systems/fence/fence-system.d.ts +3 -0
- package/dist/systems/fence/fence-system.d.ts.map +1 -1
- package/dist/systems/fence/fence-system.js +10 -6
- package/dist/systems/floor-elevation/floor-elevation-system.d.ts +20 -0
- package/dist/systems/floor-elevation/floor-elevation-system.d.ts.map +1 -0
- package/dist/systems/floor-elevation/floor-elevation-system.js +78 -0
- package/dist/systems/geometry/geometry-system.d.ts +34 -0
- package/dist/systems/geometry/geometry-system.d.ts.map +1 -0
- package/dist/systems/geometry/geometry-system.js +279 -0
- package/dist/systems/guide/guide-system.d.ts.map +1 -1
- package/dist/systems/guide/guide-system.js +1 -26
- package/dist/systems/interactive/interactive-system.d.ts +1 -1
- package/dist/systems/interactive/interactive-system.d.ts.map +1 -1
- package/dist/systems/item/item-system.d.ts +7 -0
- package/dist/systems/item/item-system.d.ts.map +1 -1
- package/dist/systems/item/item-system.js +8 -11
- package/dist/systems/item-light/item-light-system.d.ts +1 -1
- package/dist/systems/item-light/item-light-system.d.ts.map +1 -1
- package/dist/systems/item-light/item-light-system.js +18 -5
- package/dist/systems/roof/roof-materials.d.ts +3 -2
- package/dist/systems/roof/roof-materials.d.ts.map +1 -1
- package/dist/systems/roof/roof-materials.js +38 -14
- package/dist/systems/roof/roof-system.d.ts +27 -0
- package/dist/systems/roof/roof-system.d.ts.map +1 -1
- package/dist/systems/roof/roof-system.js +411 -71
- package/dist/systems/scan/scan-system.d.ts.map +1 -1
- package/dist/systems/scan/scan-system.js +1 -26
- package/dist/systems/slab/slab-system.d.ts.map +1 -1
- package/dist/systems/slab/slab-system.js +167 -49
- package/dist/systems/slab/slab-system.test.d.ts +2 -0
- package/dist/systems/slab/slab-system.test.d.ts.map +1 -0
- package/dist/systems/slab/slab-system.test.js +62 -0
- package/dist/systems/stair/stair-materials.d.ts +4 -3
- package/dist/systems/stair/stair-materials.d.ts.map +1 -1
- package/dist/systems/stair/stair-materials.js +30 -15
- package/dist/systems/stair/stair-system.d.ts.map +1 -1
- package/dist/systems/stair/stair-system.js +58 -71
- package/dist/systems/surface-hole-geometry.d.ts +3 -0
- package/dist/systems/surface-hole-geometry.d.ts.map +1 -0
- package/dist/systems/surface-hole-geometry.js +4 -0
- package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
- package/dist/systems/wall/wall-cutout.js +18 -2
- package/dist/systems/wall/wall-materials.d.ts +4 -3
- package/dist/systems/wall/wall-materials.d.ts.map +1 -1
- package/dist/systems/wall/wall-materials.js +52 -28
- package/dist/systems/wall/wall-system.d.ts +1 -1
- package/dist/systems/wall/wall-system.d.ts.map +1 -1
- package/dist/systems/wall/wall-system.js +179 -31
- package/dist/systems/window/window-system.d.ts.map +1 -1
- package/dist/systems/window/window-system.js +71 -7
- package/package.json +4 -5
- package/dist/components/renderers/building/building-renderer.d.ts +0 -5
- package/dist/components/renderers/building/building-renderer.d.ts.map +0 -1
- package/dist/components/renderers/building/building-renderer.js +0 -11
- package/dist/components/renderers/ceiling/ceiling-renderer.d.ts +0 -5
- package/dist/components/renderers/ceiling/ceiling-renderer.d.ts.map +0 -1
- package/dist/components/renderers/ceiling/ceiling-renderer.js +0 -58
- package/dist/components/renderers/column/column-renderer.d.ts +0 -5
- package/dist/components/renderers/column/column-renderer.d.ts.map +0 -1
- package/dist/components/renderers/column/column-renderer.js +0 -594
- package/dist/components/renderers/door/door-renderer.d.ts +0 -5
- package/dist/components/renderers/door/door-renderer.d.ts.map +0 -1
- package/dist/components/renderers/door/door-renderer.js +0 -16
- package/dist/components/renderers/fence/fence-renderer.d.ts +0 -5
- package/dist/components/renderers/fence/fence-renderer.d.ts.map +0 -1
- package/dist/components/renderers/fence/fence-renderer.js +0 -29
- package/dist/components/renderers/guide/guide-renderer.d.ts +0 -5
- package/dist/components/renderers/guide/guide-renderer.d.ts.map +0 -1
- package/dist/components/renderers/guide/guide-renderer.js +0 -38
- package/dist/components/renderers/item/item-renderer.d.ts +0 -5
- package/dist/components/renderers/item/item-renderer.d.ts.map +0 -1
- package/dist/components/renderers/item/item-renderer.js +0 -172
- package/dist/components/renderers/level/level-renderer.d.ts +0 -5
- package/dist/components/renderers/level/level-renderer.d.ts.map +0 -1
- package/dist/components/renderers/level/level-renderer.js +0 -11
- package/dist/components/renderers/roof/roof-materials.d.ts +0 -4
- package/dist/components/renderers/roof/roof-materials.d.ts.map +0 -1
- package/dist/components/renderers/roof/roof-materials.js +0 -16
- package/dist/components/renderers/roof/roof-renderer.d.ts +0 -5
- package/dist/components/renderers/roof/roof-renderer.d.ts.map +0 -1
- package/dist/components/renderers/roof/roof-renderer.js +0 -32
- package/dist/components/renderers/roof-segment/roof-segment-renderer.d.ts +0 -5
- package/dist/components/renderers/roof-segment/roof-segment-renderer.d.ts.map +0 -1
- package/dist/components/renderers/roof-segment/roof-segment-renderer.js +0 -40
- package/dist/components/renderers/scan/scan-renderer.d.ts +0 -5
- package/dist/components/renderers/scan/scan-renderer.d.ts.map +0 -1
- package/dist/components/renderers/scan/scan-renderer.js +0 -54
- package/dist/components/renderers/site/site-renderer.d.ts +0 -5
- package/dist/components/renderers/site/site-renderer.d.ts.map +0 -1
- package/dist/components/renderers/site/site-renderer.js +0 -106
- package/dist/components/renderers/slab/slab-renderer.d.ts +0 -5
- package/dist/components/renderers/slab/slab-renderer.d.ts.map +0 -1
- package/dist/components/renderers/slab/slab-renderer.js +0 -58
- package/dist/components/renderers/spawn/spawn-renderer.d.ts +0 -5
- package/dist/components/renderers/spawn/spawn-renderer.d.ts.map +0 -1
- package/dist/components/renderers/spawn/spawn-renderer.js +0 -32
- package/dist/components/renderers/stair/stair-renderer.d.ts +0 -5
- package/dist/components/renderers/stair/stair-renderer.d.ts.map +0 -1
- package/dist/components/renderers/stair/stair-renderer.js +0 -603
- package/dist/components/renderers/stair-segment/stair-segment-renderer.d.ts +0 -5
- package/dist/components/renderers/stair-segment/stair-segment-renderer.d.ts.map +0 -1
- package/dist/components/renderers/stair-segment/stair-segment-renderer.js +0 -32
- package/dist/components/renderers/wall/wall-renderer.d.ts +0 -5
- package/dist/components/renderers/wall/wall-renderer.d.ts.map +0 -1
- package/dist/components/renderers/wall/wall-renderer.js +0 -16
- package/dist/components/renderers/window/window-renderer.d.ts +0 -5
- package/dist/components/renderers/window/window-renderer.d.ts.map +0 -1
- package/dist/components/renderers/window/window-renderer.js +0 -21
- package/dist/components/renderers/zone/zone-renderer.d.ts +0 -5
- package/dist/components/renderers/zone/zone-renderer.d.ts.map +0 -1
- package/dist/components/renderers/zone/zone-renderer.js +0 -181
- package/dist/lib/suppress-three-clock-warning.d.ts +0 -25
- package/dist/lib/suppress-three-clock-warning.d.ts.map +0 -1
- package/dist/lib/suppress-three-clock-warning.js +0 -51
- package/dist/systems/export/export-system.d.ts +0 -2
- package/dist/systems/export/export-system.d.ts.map +0 -1
- package/dist/systems/export/export-system.js +0 -60
- package/dist/systems/item-mesh-metadata/compute-item-mesh-metadata.d.ts +0 -14
- package/dist/systems/item-mesh-metadata/compute-item-mesh-metadata.d.ts.map +0 -1
- package/dist/systems/item-mesh-metadata/compute-item-mesh-metadata.js +0 -155
- package/dist/systems/item-mesh-metadata/item-mesh-metadata-system.d.ts +0 -6
- package/dist/systems/item-mesh-metadata/item-mesh-metadata-system.d.ts.map +0 -1
- package/dist/systems/item-mesh-metadata/item-mesh-metadata-system.js +0 -75
- package/dist/systems/item-mesh-metadata/sync-request.d.ts +0 -7
- package/dist/systems/item-mesh-metadata/sync-request.d.ts.map +0 -1
- package/dist/systems/item-mesh-metadata/sync-request.js +0 -25
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { calculateLevelMiters, DEFAULT_WALL_HEIGHT, getAdjacentWallIds, getWallCurveFrameAt, getWallMiterBoundaryPoints, getWallPlanFootprint, getWallSurfacePolygon, getWallThickness, isCurvedWall, pointToKey, resolveLevelId, sceneRegistry, spatialGridManager, useScene, } from '@pascal-app/core';
|
|
1
|
+
import { calculateLevelMiters, DEFAULT_WALL_HEIGHT, getAdjacentWallIds, getEffectiveNode, getWallCurveFrameAt, getWallMiterBoundaryPoints, getWallPlanFootprint, getWallSurfacePolygon, getWallThickness, isCurvedWall, pointToKey, resolveLevelId, sceneRegistry, spatialGridManager, useLiveNodeOverrides, useLiveTransforms, useScene, } from '@pascal-app/core';
|
|
2
2
|
import { useFrame } from '@react-three/fiber';
|
|
3
3
|
import * as THREE from 'three';
|
|
4
4
|
import { Brush, Evaluator, SUBTRACTION } from 'three-bvh-csg';
|
|
@@ -13,6 +13,9 @@ function computeGeometryBoundsTree(geometry) {
|
|
|
13
13
|
geometry.computeBoundsTree = computeBoundsTree;
|
|
14
14
|
geometry.computeBoundsTree({ maxLeafSize: 10 });
|
|
15
15
|
}
|
|
16
|
+
function csgGeometry(brush) {
|
|
17
|
+
return brush.geometry;
|
|
18
|
+
}
|
|
16
19
|
function ensureUv2Attribute(geometry) {
|
|
17
20
|
const uv = geometry.getAttribute('uv');
|
|
18
21
|
if (!uv)
|
|
@@ -20,7 +23,7 @@ function ensureUv2Attribute(geometry) {
|
|
|
20
23
|
geometry.setAttribute('uv2', new THREE.Float32BufferAttribute(Array.from(uv.array), 2));
|
|
21
24
|
}
|
|
22
25
|
function insetCurvedWallBoundaryPointsFor3D(wall, boundaryPoints, miterData) {
|
|
23
|
-
if (!boundaryPoints
|
|
26
|
+
if (!(boundaryPoints && isCurvedWall(wall))) {
|
|
24
27
|
return boundaryPoints;
|
|
25
28
|
}
|
|
26
29
|
const insetDistance = Math.min(CURVED_WALL_3D_ENDPOINT_INSET, Math.max((wall.thickness ?? 0.1) * 0.01, 0.0005));
|
|
@@ -205,49 +208,158 @@ function assignWallMaterialGroups(geometry, wall, boundaryEdges) {
|
|
|
205
208
|
// WALL SYSTEM
|
|
206
209
|
// ============================================================================
|
|
207
210
|
let useFrameNb = 0;
|
|
211
|
+
// ─── Drag-throttle state (singleton — one WallSystem mounted globally) ──
|
|
212
|
+
//
|
|
213
|
+
// Endpoint drags fire `markDirty(wallId)` on every pointermove tick. Without
|
|
214
|
+
// throttling, each tick rebuilds the dragged wall (~1 CSG + miter pass) AND
|
|
215
|
+
// every adjacent wall sharing a corner (3–4× in a t-junction or room).
|
|
216
|
+
// Visible as drag lag, especially on walls with door/window cutouts.
|
|
217
|
+
//
|
|
218
|
+
// Strategy: rebuild the dragged wall every tick (so the drag follows the
|
|
219
|
+
// cursor with full fidelity), but defer adjacent rebuilds to a trailing-
|
|
220
|
+
// edge flush DRAG_FLUSH_MS after the dirty stream stops. Visually, neighbor
|
|
221
|
+
// corners stay at their pre-drag miter until release, then snap into place
|
|
222
|
+
// within ~80ms. Standard CAD-app behavior. Speeds up t-junction drags ~3×,
|
|
223
|
+
// 4-corner-room drags ~4×.
|
|
224
|
+
const DRAG_FLUSH_MS = 80;
|
|
225
|
+
const MAX_WALL_REBUILDS_PER_FRAME = 8;
|
|
226
|
+
const WALL_PROGRESSIVE_DIRTY_THRESHOLD = MAX_WALL_REBUILDS_PER_FRAME;
|
|
227
|
+
const WALL_PROGRESSIVE_TIME_BUDGET_MS = 8;
|
|
228
|
+
let lastWallDirtyAtMs = 0;
|
|
229
|
+
const pendingAdjacentByLevel = new Map();
|
|
230
|
+
function getPendingAdjacentCount() {
|
|
231
|
+
let count = 0;
|
|
232
|
+
for (const ids of pendingAdjacentByLevel.values()) {
|
|
233
|
+
count += ids.size;
|
|
234
|
+
}
|
|
235
|
+
return count;
|
|
236
|
+
}
|
|
208
237
|
export const WallSystem = () => {
|
|
209
238
|
const dirtyNodes = useScene((state) => state.dirtyNodes);
|
|
210
239
|
const clearDirty = useScene((state) => state.clearDirty);
|
|
240
|
+
// Subscribe so override-only changes (no scene write) still re-run
|
|
241
|
+
// this component, which lets the gate below pick up the latest
|
|
242
|
+
// `dirtyNodes` set from the same render pass that received the
|
|
243
|
+
// override-publishing `markDirty` call. Without this, very fast
|
|
244
|
+
// drags could land an override and a markDirty in the same React
|
|
245
|
+
// tick and the next `useFrame` would still see the stale closure.
|
|
246
|
+
useLiveNodeOverrides((s) => s.overrides);
|
|
211
247
|
useFrame(() => {
|
|
212
|
-
|
|
248
|
+
const hasDirty = dirtyNodes.size > 0;
|
|
249
|
+
const hasPending = pendingAdjacentByLevel.size > 0;
|
|
250
|
+
if (!hasDirty && !hasPending)
|
|
213
251
|
return;
|
|
214
252
|
const nodes = useScene.getState().nodes;
|
|
253
|
+
const now = performance.now();
|
|
215
254
|
// Collect dirty walls and their levels
|
|
216
255
|
const dirtyWallsByLevel = new Map();
|
|
256
|
+
let dirtyWallCount = 0;
|
|
217
257
|
useFrameNb += 1;
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
dirtyWallsByLevel.
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
258
|
+
if (hasDirty) {
|
|
259
|
+
dirtyNodes.forEach((id) => {
|
|
260
|
+
const node = nodes[id];
|
|
261
|
+
if (!node || node.type !== 'wall')
|
|
262
|
+
return;
|
|
263
|
+
const levelId = node.parentId;
|
|
264
|
+
if (!levelId)
|
|
265
|
+
return;
|
|
266
|
+
if (!dirtyWallsByLevel.has(levelId)) {
|
|
267
|
+
dirtyWallsByLevel.set(levelId, new Set());
|
|
268
|
+
}
|
|
269
|
+
dirtyWallsByLevel.get(levelId)?.add(id);
|
|
270
|
+
dirtyWallCount += 1;
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
const hasDirtyWalls = dirtyWallsByLevel.size > 0;
|
|
274
|
+
if (hasDirtyWalls) {
|
|
275
|
+
lastWallDirtyAtMs = now;
|
|
276
|
+
}
|
|
277
|
+
const useProgressiveWallRebuilds = dirtyWallCount > WALL_PROGRESSIVE_DIRTY_THRESHOLD;
|
|
278
|
+
let rebuiltWallsThisFrame = 0;
|
|
279
|
+
const rebuildFrameStartedAt = now;
|
|
230
280
|
// Process each level that has dirty walls
|
|
231
281
|
for (const [levelId, dirtyWallIds] of dirtyWallsByLevel) {
|
|
282
|
+
if (useProgressiveWallRebuilds && rebuiltWallsThisFrame >= MAX_WALL_REBUILDS_PER_FRAME) {
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
232
285
|
const levelWalls = getLevelWalls(levelId);
|
|
233
286
|
const miterData = calculateLevelMiters(levelWalls);
|
|
234
|
-
|
|
287
|
+
const rebuiltWallIds = new Set();
|
|
288
|
+
// Update dirty walls — always, no throttling. The dragged wall must
|
|
289
|
+
// follow the cursor with full fidelity (cutouts and all). Large imports
|
|
290
|
+
// enter the progressive path so initial load can't lock the tab.
|
|
235
291
|
for (const wallId of dirtyWallIds) {
|
|
292
|
+
if (useProgressiveWallRebuilds) {
|
|
293
|
+
if (rebuiltWallsThisFrame >= MAX_WALL_REBUILDS_PER_FRAME) {
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
if (rebuiltWallsThisFrame > 0 &&
|
|
297
|
+
performance.now() - rebuildFrameStartedAt >= WALL_PROGRESSIVE_TIME_BUDGET_MS) {
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
236
301
|
const mesh = sceneRegistry.nodes.get(wallId);
|
|
237
302
|
if (mesh) {
|
|
238
303
|
updateWallGeometry(wallId, miterData);
|
|
239
304
|
clearDirty(wallId);
|
|
305
|
+
rebuiltWallIds.add(wallId);
|
|
306
|
+
rebuiltWallsThisFrame += 1;
|
|
240
307
|
}
|
|
241
308
|
// If mesh not found, keep it dirty for next frame
|
|
242
309
|
}
|
|
243
|
-
|
|
244
|
-
|
|
310
|
+
if (rebuiltWallIds.size === 0) {
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
// Adjacent walls sharing junctions — *defer* during active drag
|
|
314
|
+
// (dirty arrived this frame), flush on the trailing edge.
|
|
315
|
+
const adjacentWallIds = getAdjacentWallIds(levelWalls, rebuiltWallIds);
|
|
316
|
+
let pending = pendingAdjacentByLevel.get(levelId);
|
|
317
|
+
if (!pending) {
|
|
318
|
+
pending = new Set();
|
|
319
|
+
pendingAdjacentByLevel.set(levelId, pending);
|
|
320
|
+
}
|
|
245
321
|
for (const wallId of adjacentWallIds) {
|
|
246
322
|
if (!dirtyWallIds.has(wallId)) {
|
|
323
|
+
pending.add(wallId);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
// Trailing-edge flush: if no new dirty marks for DRAG_FLUSH_MS, the
|
|
328
|
+
// drag has ended — rebuild the queued neighbors so corners snap into
|
|
329
|
+
// their correct miter joins.
|
|
330
|
+
const quiet = !hasDirtyWalls && now - lastWallDirtyAtMs >= DRAG_FLUSH_MS;
|
|
331
|
+
if (quiet && pendingAdjacentByLevel.size > 0) {
|
|
332
|
+
const pendingCount = getPendingAdjacentCount();
|
|
333
|
+
const useProgressiveAdjacentRebuilds = pendingCount > WALL_PROGRESSIVE_DIRTY_THRESHOLD;
|
|
334
|
+
let rebuiltAdjacentThisFrame = 0;
|
|
335
|
+
const adjacentFrameStartedAt = performance.now();
|
|
336
|
+
for (const [levelId, pendingIds] of pendingAdjacentByLevel) {
|
|
337
|
+
if (pendingIds.size === 0)
|
|
338
|
+
continue;
|
|
339
|
+
const levelWalls = getLevelWalls(levelId);
|
|
340
|
+
const miterData = calculateLevelMiters(levelWalls);
|
|
341
|
+
for (const wallId of Array.from(pendingIds)) {
|
|
342
|
+
if (useProgressiveAdjacentRebuilds) {
|
|
343
|
+
if (rebuiltAdjacentThisFrame >= MAX_WALL_REBUILDS_PER_FRAME) {
|
|
344
|
+
break;
|
|
345
|
+
}
|
|
346
|
+
if (rebuiltAdjacentThisFrame > 0 &&
|
|
347
|
+
performance.now() - adjacentFrameStartedAt >= WALL_PROGRESSIVE_TIME_BUDGET_MS) {
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
247
351
|
const mesh = sceneRegistry.nodes.get(wallId);
|
|
248
|
-
if (mesh)
|
|
352
|
+
if (mesh)
|
|
249
353
|
updateWallGeometry(wallId, miterData);
|
|
250
|
-
|
|
354
|
+
pendingIds.delete(wallId);
|
|
355
|
+
rebuiltAdjacentThisFrame += 1;
|
|
356
|
+
}
|
|
357
|
+
if (pendingIds.size === 0) {
|
|
358
|
+
pendingAdjacentByLevel.delete(levelId);
|
|
359
|
+
}
|
|
360
|
+
if (useProgressiveAdjacentRebuilds &&
|
|
361
|
+
rebuiltAdjacentThisFrame >= MAX_WALL_REBUILDS_PER_FRAME) {
|
|
362
|
+
break;
|
|
251
363
|
}
|
|
252
364
|
}
|
|
253
365
|
}
|
|
@@ -255,7 +367,22 @@ export const WallSystem = () => {
|
|
|
255
367
|
return null;
|
|
256
368
|
};
|
|
257
369
|
/**
|
|
258
|
-
*
|
|
370
|
+
* Merge any live override for a wall into the scene record. Lets the
|
|
371
|
+
* 2D move handler publish `{ start, end, curveOffset }` to
|
|
372
|
+
* `useLiveNodeOverrides` and have the geometry / miter pipeline use
|
|
373
|
+
* those values without zustand churn during the drag. When no
|
|
374
|
+
* override is set, the wall is returned unchanged.
|
|
375
|
+
*/
|
|
376
|
+
function getEffectiveWall(wall) {
|
|
377
|
+
const override = useLiveNodeOverrides.getState().get(wall.id);
|
|
378
|
+
if (!override || Object.keys(override).length === 0)
|
|
379
|
+
return wall;
|
|
380
|
+
return { ...wall, ...override };
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Gets all walls that belong to a level, with any live overrides
|
|
384
|
+
* merged in so miters compute against the cursor-driven positions
|
|
385
|
+
* (not the pre-drag scene state).
|
|
259
386
|
*/
|
|
260
387
|
function getLevelWalls(levelId) {
|
|
261
388
|
const { nodes } = useScene.getState();
|
|
@@ -266,28 +393,49 @@ function getLevelWalls(levelId) {
|
|
|
266
393
|
for (const childId of level.children) {
|
|
267
394
|
const child = nodes[childId];
|
|
268
395
|
if (child?.type === 'wall') {
|
|
269
|
-
walls.push(child);
|
|
396
|
+
walls.push(getEffectiveWall(child));
|
|
270
397
|
}
|
|
271
398
|
}
|
|
272
399
|
return walls;
|
|
273
400
|
}
|
|
274
401
|
/**
|
|
275
|
-
* Updates the geometry for a single wall
|
|
402
|
+
* Updates the geometry for a single wall. Reads the effective node
|
|
403
|
+
* (override-merged) so a 2D drag visibly moves the 3D mesh without
|
|
404
|
+
* having touched `useScene` mid-drag.
|
|
276
405
|
*/
|
|
277
406
|
function updateWallGeometry(wallId, miterData) {
|
|
278
407
|
const nodes = useScene.getState().nodes;
|
|
279
|
-
const
|
|
280
|
-
if (!
|
|
408
|
+
const sceneNode = nodes[wallId];
|
|
409
|
+
if (!sceneNode || sceneNode.type !== 'wall')
|
|
281
410
|
return;
|
|
411
|
+
const node = getEffectiveWall(sceneNode);
|
|
282
412
|
const mesh = sceneRegistry.nodes.get(wallId);
|
|
283
413
|
if (!mesh)
|
|
284
414
|
return;
|
|
285
415
|
const levelId = resolveLevelId(node, nodes);
|
|
286
|
-
const slabElevation = spatialGridManager.getSlabElevationForWall(levelId, node.start, node.end);
|
|
416
|
+
const slabElevation = spatialGridManager.getSlabElevationForWall(levelId, node.start, node.end, node.curveOffset ?? 0, node.thickness);
|
|
287
417
|
const childrenIds = node.children || [];
|
|
418
|
+
// Merge live overrides into door / window children so cutouts track an
|
|
419
|
+
// in-flight resize drag (door width arrow, window height arrow, etc.)
|
|
420
|
+
// without waiting on the scene store. Non-cutout children pass through
|
|
421
|
+
// unchanged.
|
|
288
422
|
const childrenNodes = childrenIds
|
|
289
423
|
.map((childId) => nodes[childId])
|
|
290
|
-
.filter((n) => n !== undefined)
|
|
424
|
+
.filter((n) => n !== undefined)
|
|
425
|
+
.map((child) => {
|
|
426
|
+
if (child.type !== 'door' && child.type !== 'window')
|
|
427
|
+
return child;
|
|
428
|
+
// `getEffectiveNode` folds in resize overrides (width/height arrows).
|
|
429
|
+
// Position moves publish to `useLiveTransforms` instead, so fold that
|
|
430
|
+
// in too — otherwise shaped openings (arch/rounded/`opening`), whose
|
|
431
|
+
// cutout brush is rebuilt from `node.position`, lag the live move
|
|
432
|
+
// (rectangular cutouts already track via the live mesh matrixWorld).
|
|
433
|
+
const effective = getEffectiveNode(child);
|
|
434
|
+
const live = useLiveTransforms.getState().get(child.id);
|
|
435
|
+
if (!live?.position)
|
|
436
|
+
return effective;
|
|
437
|
+
return { ...effective, position: live.position };
|
|
438
|
+
});
|
|
291
439
|
const newGeo = generateExtrudedWall(node, childrenNodes, miterData, slabElevation);
|
|
292
440
|
mesh.geometry.dispose();
|
|
293
441
|
mesh.geometry = newGeo;
|
|
@@ -380,16 +528,16 @@ export function generateExtrudedWall(wallNode, childrenNodes, miterData, slabEle
|
|
|
380
528
|
cutoutBrush.updateMatrixWorld();
|
|
381
529
|
const newResult = csgEvaluator.evaluate(resultBrush, cutoutBrush, SUBTRACTION);
|
|
382
530
|
if (resultBrush !== wallBrush) {
|
|
383
|
-
resultBrush.
|
|
531
|
+
csgGeometry(resultBrush).dispose();
|
|
384
532
|
}
|
|
385
533
|
resultBrush = newResult;
|
|
386
534
|
}
|
|
387
535
|
// Clean up
|
|
388
|
-
wallBrush.
|
|
536
|
+
csgGeometry(wallBrush).dispose();
|
|
389
537
|
for (const brush of cutoutBrushes) {
|
|
390
|
-
brush.
|
|
538
|
+
csgGeometry(brush).dispose();
|
|
391
539
|
}
|
|
392
|
-
const resultGeometry = resultBrush
|
|
540
|
+
const resultGeometry = csgGeometry(resultBrush);
|
|
393
541
|
resultGeometry.computeVertexNormals();
|
|
394
542
|
assignWallMaterialGroups(resultGeometry, wallNode, boundaryEdges);
|
|
395
543
|
ensureUv2Attribute(resultGeometry);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"window-system.d.ts","sourceRoot":"","sources":["../../../src/systems/window/window-system.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"window-system.d.ts","sourceRoot":"","sources":["../../../src/systems/window/window-system.tsx"],"names":[],"mappings":"AAuBA,eAAO,MAAM,yBAAyB,yBAAyB,CAAA;AAC/D,eAAO,MAAM,8BAA8B,8BAA8B,CAAA;AACzE,eAAO,MAAM,+BAA+B,+BAA+B,CAAA;AAC3E,eAAO,MAAM,gCAAgC,gCAAgC,CAAA;AAC7E,eAAO,MAAM,4BAA4B,4BAA4B,CAAA;AACrE,eAAO,MAAM,yBAAyB,yBAAyB,CAAA;AAC/D,eAAO,MAAM,4BAA4B,4BAA4B,CAAA;AACrE,eAAO,MAAM,0BAA0B,0BAA0B,CAAA;AACjE,eAAO,MAAM,uBAAuB,uBAAuB,CAAA;AAC3D,eAAO,MAAM,uBAAuB,uBAAuB,CAAA;AAM3D,eAAO,MAAM,YAAY,YA0FxB,CAAA"}
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
import { sceneRegistry, useInteractive, useScene, } from '@pascal-app/core';
|
|
1
|
+
import { getEffectiveNode, sceneRegistry, useInteractive, useLiveNodeOverrides, useScene, } from '@pascal-app/core';
|
|
2
2
|
import { useFrame } from '@react-three/fiber';
|
|
3
|
+
import { useEffect, useRef } from 'react';
|
|
3
4
|
import * as THREE from 'three';
|
|
4
|
-
import {
|
|
5
|
+
import { createSurfaceRoleMaterial, glassMaterial as defaultGlassMaterial, baseMaterial as getBaseMaterial, } from '../../lib/materials';
|
|
6
|
+
import useViewer from '../../store/use-viewer';
|
|
5
7
|
// Invisible material for root mesh — used as selection hitbox only
|
|
6
8
|
const hitboxMaterial = new THREE.MeshBasicMaterial({ visible: false });
|
|
9
|
+
let baseMaterial = getBaseMaterial();
|
|
10
|
+
let glassMaterial = defaultGlassMaterial;
|
|
7
11
|
export const CASEMENT_WINDOW_SASH_NAME = 'casement-window-sash';
|
|
8
12
|
export const FRENCH_CASEMENT_LEFT_SASH_NAME = 'french-casement-left-sash';
|
|
9
13
|
export const FRENCH_CASEMENT_RIGHT_SASH_NAME = 'french-casement-right-sash';
|
|
@@ -14,27 +18,87 @@ export const DOUBLE_HUNG_BOTTOM_SASH_NAME = 'double-hung-bottom-sash';
|
|
|
14
18
|
export const LOUVERED_WINDOW_SLATS_NAME = 'louvered-window-slats';
|
|
15
19
|
export const AWNING_WINDOW_SASH_NAME = 'awning-window-sash';
|
|
16
20
|
export const HOPPER_WINDOW_SASH_NAME = 'hopper-window-sash';
|
|
21
|
+
const MAX_WINDOW_REBUILDS_PER_FRAME = 16;
|
|
22
|
+
const WINDOW_PROGRESSIVE_DIRTY_THRESHOLD = MAX_WINDOW_REBUILDS_PER_FRAME;
|
|
23
|
+
const WINDOW_PROGRESSIVE_TIME_BUDGET_MS = 8;
|
|
17
24
|
export const WindowSystem = () => {
|
|
18
25
|
const dirtyNodes = useScene((state) => state.dirtyNodes);
|
|
19
26
|
const clearDirty = useScene((state) => state.clearDirty);
|
|
27
|
+
const shading = useViewer((state) => state.shading);
|
|
28
|
+
const textures = useViewer((state) => state.textures);
|
|
29
|
+
const colorPreset = useViewer((state) => state.colorPreset);
|
|
30
|
+
const materialRevisionRef = useRef(null);
|
|
31
|
+
// Subscribe so override-only updates re-run this component. Mirrors
|
|
32
|
+
// WallSystem + DoorSystem.
|
|
33
|
+
useLiveNodeOverrides((s) => s.overrides);
|
|
34
|
+
baseMaterial = textures
|
|
35
|
+
? getBaseMaterial(shading)
|
|
36
|
+
: createSurfaceRoleMaterial('joinery', colorPreset);
|
|
37
|
+
glassMaterial = textures
|
|
38
|
+
? defaultGlassMaterial
|
|
39
|
+
: createSurfaceRoleMaterial('glazing', colorPreset);
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
const materialRevision = `${shading}:${textures ? 'textures' : 'solid'}:${colorPreset}`;
|
|
42
|
+
if (materialRevisionRef.current === materialRevision)
|
|
43
|
+
return;
|
|
44
|
+
materialRevisionRef.current = materialRevision;
|
|
45
|
+
const nodes = useScene.getState().nodes;
|
|
46
|
+
for (const node of Object.values(nodes)) {
|
|
47
|
+
if (node?.type === 'window') {
|
|
48
|
+
useScene.getState().dirtyNodes.add(node.id);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
});
|
|
20
52
|
useFrame(() => {
|
|
21
53
|
if (dirtyNodes.size === 0)
|
|
22
54
|
return;
|
|
55
|
+
baseMaterial = textures
|
|
56
|
+
? getBaseMaterial(shading)
|
|
57
|
+
: createSurfaceRoleMaterial('joinery', colorPreset);
|
|
58
|
+
glassMaterial = textures
|
|
59
|
+
? defaultGlassMaterial
|
|
60
|
+
: createSurfaceRoleMaterial('glazing', colorPreset);
|
|
23
61
|
const nodes = useScene.getState().nodes;
|
|
62
|
+
const dirtyWindowIds = [];
|
|
24
63
|
dirtyNodes.forEach((id) => {
|
|
25
64
|
const node = nodes[id];
|
|
26
65
|
if (!node || node.type !== 'window')
|
|
27
66
|
return;
|
|
67
|
+
dirtyWindowIds.push(id);
|
|
68
|
+
});
|
|
69
|
+
const useProgressiveWindowRebuilds = dirtyWindowIds.length > WINDOW_PROGRESSIVE_DIRTY_THRESHOLD;
|
|
70
|
+
const frameStartedAt = performance.now();
|
|
71
|
+
let rebuiltWindowsThisFrame = 0;
|
|
72
|
+
for (const id of dirtyWindowIds) {
|
|
73
|
+
if (useProgressiveWindowRebuilds) {
|
|
74
|
+
if (rebuiltWindowsThisFrame >= MAX_WINDOW_REBUILDS_PER_FRAME) {
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
if (rebuiltWindowsThisFrame > 0 &&
|
|
78
|
+
performance.now() - frameStartedAt >= WINDOW_PROGRESSIVE_TIME_BUDGET_MS) {
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const node = nodes[id];
|
|
83
|
+
if (!node || node.type !== 'window')
|
|
84
|
+
continue;
|
|
28
85
|
const mesh = sceneRegistry.nodes.get(id);
|
|
29
86
|
if (!mesh)
|
|
30
|
-
|
|
31
|
-
|
|
87
|
+
continue; // Keep dirty until mesh mounts
|
|
88
|
+
// Merge any live override (width / height / position) so the mesh
|
|
89
|
+
// rebuild reflects the in-flight drag without zustand churn.
|
|
90
|
+
const effectiveNode = getEffectiveNode(node);
|
|
91
|
+
updateWindowMesh(effectiveNode, mesh);
|
|
32
92
|
clearDirty(id);
|
|
93
|
+
rebuiltWindowsThisFrame += 1;
|
|
33
94
|
// Rebuild the parent wall so its cutout reflects the updated window geometry
|
|
34
|
-
|
|
35
|
-
|
|
95
|
+
// Avoid triggering expensive wall CSG rebuilds while the window is being interactively moved/duplicated.
|
|
96
|
+
// The editor tools will request a final wall rebuild on commit.
|
|
97
|
+
const isTransient = !!node.metadata?.isTransient;
|
|
98
|
+
if (!isTransient && effectiveNode.parentId) {
|
|
99
|
+
useScene.getState().dirtyNodes.add(effectiveNode.parentId);
|
|
36
100
|
}
|
|
37
|
-
}
|
|
101
|
+
}
|
|
38
102
|
}, 3);
|
|
39
103
|
return null;
|
|
40
104
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pascal-app/viewer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "3D viewer component for Pascal building editor",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -22,24 +22,23 @@
|
|
|
22
22
|
"prepublishOnly": "npm run build"
|
|
23
23
|
},
|
|
24
24
|
"peerDependencies": {
|
|
25
|
-
"@pascal-app/core": "^0.
|
|
25
|
+
"@pascal-app/core": "^0.9.1",
|
|
26
26
|
"@react-three/drei": "^10",
|
|
27
27
|
"@react-three/fiber": "^9",
|
|
28
28
|
"react": "^18 || ^19",
|
|
29
29
|
"three": "^0.184"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"polygon-clipping": "^0.15.7",
|
|
33
32
|
"three-bvh-csg": "^0.0.18",
|
|
34
33
|
"three-mesh-bvh": "^0.9.8",
|
|
35
34
|
"zustand": "^5"
|
|
36
35
|
},
|
|
37
36
|
"devDependencies": {
|
|
38
37
|
"@pascal/typescript-config": "*",
|
|
39
|
-
"@types/node": "^
|
|
38
|
+
"@types/node": "^22",
|
|
40
39
|
"@types/react": "^19.2.2",
|
|
41
40
|
"@types/three": "^0.184.0",
|
|
42
|
-
"typescript": "
|
|
41
|
+
"typescript": "6.0.3"
|
|
43
42
|
},
|
|
44
43
|
"keywords": [
|
|
45
44
|
"3d",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"building-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/building/building-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAe,MAAM,kBAAkB,CAAA;AAMjE,eAAO,MAAM,gBAAgB,GAAI,UAAU;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,4CAkBhE,CAAA"}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useRegistry } from '@pascal-app/core';
|
|
3
|
-
import { useRef } from 'react';
|
|
4
|
-
import { useNodeEvents } from '../../../hooks/use-node-events';
|
|
5
|
-
import { NodeRenderer } from '../node-renderer';
|
|
6
|
-
export const BuildingRenderer = ({ node }) => {
|
|
7
|
-
const ref = useRef(null);
|
|
8
|
-
useRegistry(node.id, node.type, ref);
|
|
9
|
-
const handlers = useNodeEvents(node, 'building');
|
|
10
|
-
return (_jsx("group", { position: node.position, ref: ref, rotation: [node.rotation[0], node.rotation[1], node.rotation[2]], ...handlers, children: node.children.map((childId) => (_jsx(NodeRenderer, { nodeId: childId }, childId))) }));
|
|
11
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ceiling-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/ceiling/ceiling-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,WAAW,EAIjB,MAAM,kBAAkB,CAAA;AA8CzB,eAAO,MAAM,eAAe,GAAI,UAAU;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,4CAoC9D,CAAA"}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { getMaterialPresetByRef, resolveMaterial, useRegistry, } from '@pascal-app/core';
|
|
3
|
-
import { useMemo, useRef } from 'react';
|
|
4
|
-
import { float, mix, positionWorld, smoothstep } from 'three/tsl';
|
|
5
|
-
import { BackSide, FrontSide, MeshBasicNodeMaterial } from 'three/webgpu';
|
|
6
|
-
import { useNodeEvents } from '../../../hooks/use-node-events';
|
|
7
|
-
import { NodeRenderer } from '../node-renderer';
|
|
8
|
-
const gridScale = 5;
|
|
9
|
-
const gridX = positionWorld.x.mul(gridScale).fract();
|
|
10
|
-
const gridY = positionWorld.z.mul(gridScale).fract();
|
|
11
|
-
const lineWidth = 0.05;
|
|
12
|
-
const lineX = smoothstep(lineWidth, 0, gridX).add(smoothstep(1.0 - lineWidth, 1.0, gridX));
|
|
13
|
-
const lineY = smoothstep(lineWidth, 0, gridY).add(smoothstep(1.0 - lineWidth, 1.0, gridY));
|
|
14
|
-
const gridPattern = lineX.max(lineY);
|
|
15
|
-
const gridOpacity = mix(float(0.2), float(0.6), gridPattern);
|
|
16
|
-
function createCeilingMaterials(color = '#999999') {
|
|
17
|
-
const topMaterial = new MeshBasicNodeMaterial({
|
|
18
|
-
color,
|
|
19
|
-
transparent: true,
|
|
20
|
-
depthWrite: false,
|
|
21
|
-
side: FrontSide,
|
|
22
|
-
});
|
|
23
|
-
topMaterial.opacityNode = gridOpacity;
|
|
24
|
-
const bottomMaterial = new MeshBasicNodeMaterial({
|
|
25
|
-
color,
|
|
26
|
-
transparent: true,
|
|
27
|
-
side: BackSide,
|
|
28
|
-
});
|
|
29
|
-
return { topMaterial, bottomMaterial };
|
|
30
|
-
}
|
|
31
|
-
const ceilingMaterialCache = new Map();
|
|
32
|
-
function getCeilingMaterials(color = '#999999') {
|
|
33
|
-
const cacheKey = color;
|
|
34
|
-
const cached = ceilingMaterialCache.get(cacheKey);
|
|
35
|
-
if (cached)
|
|
36
|
-
return cached;
|
|
37
|
-
const materials = createCeilingMaterials(color);
|
|
38
|
-
ceilingMaterialCache.set(cacheKey, materials);
|
|
39
|
-
return materials;
|
|
40
|
-
}
|
|
41
|
-
export const CeilingRenderer = ({ node }) => {
|
|
42
|
-
const ref = useRef(null);
|
|
43
|
-
useRegistry(node.id, 'ceiling', ref);
|
|
44
|
-
const handlers = useNodeEvents(node, 'ceiling');
|
|
45
|
-
const materials = useMemo(() => {
|
|
46
|
-
const preset = getMaterialPresetByRef(node.materialPreset);
|
|
47
|
-
const props = preset?.mapProperties ?? resolveMaterial(node.material);
|
|
48
|
-
const color = props.color || '#999999';
|
|
49
|
-
return getCeilingMaterials(color);
|
|
50
|
-
}, [
|
|
51
|
-
node.materialPreset,
|
|
52
|
-
node.material,
|
|
53
|
-
node.material?.preset,
|
|
54
|
-
node.material?.properties,
|
|
55
|
-
node.material?.texture,
|
|
56
|
-
]);
|
|
57
|
-
return (_jsxs("mesh", { material: materials.bottomMaterial, ref: ref, children: [_jsx("boxGeometry", { args: [0, 0, 0] }), _jsx("mesh", { material: materials.topMaterial, name: "ceiling-grid", ...handlers, scale: 0, visible: false, children: _jsx("boxGeometry", { args: [0, 0, 0] }) }), node.children.map((childId) => (_jsx(NodeRenderer, { nodeId: childId }, childId)))] }));
|
|
58
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"column-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/column/column-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAkC,MAAM,kBAAkB,CAAA;AA64ClF,eAAO,MAAM,cAAc,GAAI,UAAU;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,4CA0E5D,CAAA"}
|