@pascal-app/core 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/events/bus.d.ts +45 -6
- package/dist/events/bus.d.ts.map +1 -1
- package/dist/events/bus.js +1 -1
- package/dist/hooks/scene-registry/scene-registry.d.ts +2 -0
- package/dist/hooks/scene-registry/scene-registry.d.ts.map +1 -1
- package/dist/hooks/scene-registry/scene-registry.js +2 -0
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-manager.js +164 -6
- package/dist/index.d.ts +8 -15
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -14
- package/dist/lib/door-operation.d.ts +7 -0
- package/dist/lib/door-operation.d.ts.map +1 -0
- package/dist/lib/door-operation.js +25 -0
- package/dist/lib/polygon-geometry.d.ts.map +1 -1
- package/dist/lib/slab-polygon.d.ts +3 -0
- package/dist/lib/slab-polygon.d.ts.map +1 -0
- package/dist/lib/slab-polygon.js +58 -0
- package/dist/lib/space-detection.d.ts.map +1 -1
- package/dist/lib/space-detection.js +10 -8
- package/dist/material-library.d.ts +5 -3
- package/dist/material-library.d.ts.map +1 -1
- package/dist/material-library.js +28 -32
- package/dist/schema/asset-url.d.ts +34 -0
- package/dist/schema/asset-url.d.ts.map +1 -0
- package/dist/schema/asset-url.js +79 -0
- package/dist/schema/asset-url.test.d.ts +2 -0
- package/dist/schema/asset-url.test.d.ts.map +1 -0
- package/dist/schema/asset-url.test.js +134 -0
- package/dist/schema/index.d.ts +7 -5
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +5 -3
- package/dist/schema/material.d.ts +1 -0
- package/dist/schema/material.d.ts.map +1 -1
- package/dist/schema/material.js +13 -11
- package/dist/schema/nodes/column.d.ts +520 -0
- package/dist/schema/nodes/column.d.ts.map +1 -0
- package/dist/schema/nodes/column.js +385 -0
- package/dist/schema/nodes/door.d.ts +72 -0
- package/dist/schema/nodes/door.d.ts.map +1 -1
- package/dist/schema/nodes/door.js +39 -2
- package/dist/schema/nodes/fence.d.ts +1 -1
- package/dist/schema/nodes/fence.js +2 -2
- package/dist/schema/nodes/guide.d.ts +17 -0
- package/dist/schema/nodes/guide.d.ts.map +1 -1
- package/dist/schema/nodes/guide.js +11 -1
- package/dist/schema/nodes/item.d.ts +20 -0
- package/dist/schema/nodes/item.d.ts.map +1 -1
- package/dist/schema/nodes/item.js +30 -1
- package/dist/schema/nodes/level.d.ts +1 -1
- package/dist/schema/nodes/level.d.ts.map +1 -1
- package/dist/schema/nodes/level.js +6 -0
- package/dist/schema/nodes/roof-segment.d.ts +2 -2
- package/dist/schema/nodes/roof.d.ts +2 -2
- package/dist/schema/nodes/roof.d.ts.map +1 -1
- package/dist/schema/nodes/roof.js +5 -5
- package/dist/schema/nodes/scan.d.ts.map +1 -1
- package/dist/schema/nodes/scan.js +2 -1
- package/dist/schema/nodes/site.d.ts +7 -0
- package/dist/schema/nodes/site.d.ts.map +1 -1
- package/dist/schema/nodes/spawn.d.ts +24 -0
- package/dist/schema/nodes/spawn.d.ts.map +1 -0
- package/dist/schema/nodes/spawn.js +8 -0
- package/dist/schema/nodes/stair.d.ts +6 -6
- package/dist/schema/nodes/stair.d.ts.map +1 -1
- package/dist/schema/nodes/stair.js +9 -7
- package/dist/schema/nodes/window.d.ts +55 -0
- package/dist/schema/nodes/window.d.ts.map +1 -1
- package/dist/schema/nodes/window.js +29 -0
- package/dist/schema/types.d.ts +320 -5
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/types.js +4 -0
- package/dist/store/actions/node-actions.d.ts.map +1 -1
- package/dist/store/actions/node-actions.js +13 -10
- package/dist/store/use-interactive.d.ts +43 -0
- package/dist/store/use-interactive.d.ts.map +1 -1
- package/dist/store/use-interactive.js +66 -0
- package/dist/store/use-scene.d.ts.map +1 -1
- package/dist/store/use-scene.js +69 -5
- package/dist/systems/stair/stair-opening-sync.d.ts.map +1 -1
- package/dist/systems/stair/stair-opening-sync.js +41 -7
- package/dist/systems/stair/stair-opening-sync.test.d.ts +2 -0
- package/dist/systems/stair/stair-opening-sync.test.d.ts.map +1 -0
- package/dist/systems/stair/stair-opening-sync.test.js +63 -0
- package/dist/systems/wall/wall-curve.d.ts +1 -1
- package/dist/systems/wall/wall-curve.d.ts.map +1 -1
- package/dist/systems/wall/wall-curve.js +1 -1
- package/dist/systems/wall/wall-mitering.d.ts.map +1 -1
- package/dist/systems/wall/wall-mitering.js +2 -6
- package/package.json +34 -5
- package/dist/materials.d.ts +0 -10
- package/dist/materials.d.ts.map +0 -1
- package/dist/materials.js +0 -22
- package/dist/systems/ceiling/ceiling-system.d.ts +0 -8
- package/dist/systems/ceiling/ceiling-system.d.ts.map +0 -1
- package/dist/systems/ceiling/ceiling-system.js +0 -92
- package/dist/systems/door/door-system.d.ts +0 -2
- package/dist/systems/door/door-system.d.ts.map +0 -1
- package/dist/systems/door/door-system.js +0 -195
- package/dist/systems/fence/fence-system.d.ts +0 -2
- package/dist/systems/fence/fence-system.d.ts.map +0 -1
- package/dist/systems/fence/fence-system.js +0 -187
- package/dist/systems/item/item-system.d.ts +0 -2
- package/dist/systems/item/item-system.d.ts.map +0 -1
- package/dist/systems/item/item-system.js +0 -48
- package/dist/systems/roof/roof-system.d.ts +0 -16
- package/dist/systems/roof/roof-system.d.ts.map +0 -1
- package/dist/systems/roof/roof-system.js +0 -797
- package/dist/systems/slab/slab-system.d.ts +0 -8
- package/dist/systems/slab/slab-system.d.ts.map +0 -1
- package/dist/systems/slab/slab-system.js +0 -214
- package/dist/systems/stair/stair-system.d.ts +0 -2
- package/dist/systems/stair/stair-system.d.ts.map +0 -1
- package/dist/systems/stair/stair-system.js +0 -776
- package/dist/systems/wall/wall-system.d.ts +0 -12
- package/dist/systems/wall/wall-system.d.ts.map +0 -1
- package/dist/systems/wall/wall-system.js +0 -455
- package/dist/systems/window/window-system.d.ts +0 -2
- package/dist/systems/window/window-system.d.ts.map +0 -1
- package/dist/systems/window/window-system.js +0 -131
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-scene.d.ts","sourceRoot":"","sources":["../../src/store/use-scene.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAE1C,OAAO,EAAU,KAAK,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,SAAS,CAAA;AAEnE,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAMrE,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"use-scene.d.ts","sourceRoot":"","sources":["../../src/store/use-scene.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAE1C,OAAO,EAAU,KAAK,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,SAAS,CAAA;AAEnE,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAMrE,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAgZzD,MAAM,MAAM,UAAU,GAAG;IAEvB,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;IAGjC,WAAW,EAAE,SAAS,EAAE,CAAA;IAGxB,UAAU,EAAE,GAAG,CAAC,SAAS,CAAC,CAAA;IAG1B,WAAW,EAAE,MAAM,CAAC,YAAY,EAAE,UAAU,CAAC,CAAA;IAG7C,QAAQ,EAAE,OAAO,CAAA;IACjB,WAAW,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAA;IAGxC,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,UAAU,EAAE,MAAM,IAAI,CAAA;IACtB,WAAW,EAAE,MAAM,IAAI,CAAA;IACvB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,WAAW,EAAE,SAAS,EAAE,KAAK,IAAI,CAAA;IAE/E,SAAS,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,IAAI,CAAA;IAClC,UAAU,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,IAAI,CAAA;IAEnC,UAAU,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,SAAS,KAAK,IAAI,CAAA;IACzD,WAAW,EAAE,CAAC,GAAG,EAAE;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;KAAE,EAAE,KAAK,IAAI,CAAA;IAErE,UAAU,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,CAAA;IAC3D,WAAW,EAAE,CAAC,OAAO,EAAE;QAAE,EAAE,EAAE,SAAS,CAAC;QAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;KAAE,EAAE,KAAK,IAAI,CAAA;IAE3E,UAAU,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,IAAI,CAAA;IACnC,WAAW,EAAE,CAAC,GAAG,EAAE,SAAS,EAAE,KAAK,IAAI,CAAA;IAGvC,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,KAAK,YAAY,CAAA;IACvE,gBAAgB,EAAE,CAAC,EAAE,EAAE,YAAY,KAAK,IAAI,CAAA;IAC5C,gBAAgB,EAAE,CAAC,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,KAAK,IAAI,CAAA;IACnF,eAAe,EAAE,CAAC,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,KAAK,IAAI,CAAA;IAC9D,oBAAoB,EAAE,CAAC,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,KAAK,IAAI,CAAA;CACpE,CAAA;AAID,KAAK,aAAa,GAAG,aAAa,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,GAAG;IACzD,QAAQ,EAAE,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,GAAG,aAAa,GAAG,aAAa,CAAC,CAAC,CAAC,CAAA;CAC7F,CAAA;AAED,QAAA,MAAM,QAAQ,EAAE,aA+Of,CAAA;AAED,eAAe,QAAQ,CAAA;AAOvB,wBAAgB,iBAAiB,SAMhC"}
|
package/dist/store/use-scene.js
CHANGED
|
@@ -7,8 +7,8 @@ import { LevelNode } from '../schema/nodes/level';
|
|
|
7
7
|
import { SiteNode } from '../schema/nodes/site';
|
|
8
8
|
import { StairNode as StairNodeSchema } from '../schema/nodes/stair';
|
|
9
9
|
import { StairSegmentNode as StairSegmentNodeSchema } from '../schema/nodes/stair-segment';
|
|
10
|
-
import { resetSceneHistoryPauseDepth } from './history-control';
|
|
11
10
|
import * as nodeActions from './actions/node-actions';
|
|
11
|
+
import { resetSceneHistoryPauseDepth } from './history-control';
|
|
12
12
|
function getFiniteNumber(value, fallback) {
|
|
13
13
|
return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
|
|
14
14
|
}
|
|
@@ -88,7 +88,7 @@ function migrateWallSurfaceMaterials(node) {
|
|
|
88
88
|
material: node.material,
|
|
89
89
|
materialPreset: typeof node.materialPreset === 'string' ? node.materialPreset : undefined,
|
|
90
90
|
};
|
|
91
|
-
if (!hasInterior
|
|
91
|
+
if (!(hasInterior || hasExterior)) {
|
|
92
92
|
if (legacyFinish.material === undefined && legacyFinish.materialPreset === undefined) {
|
|
93
93
|
return node;
|
|
94
94
|
}
|
|
@@ -139,7 +139,7 @@ function migrateStairSurfaceMaterials(node) {
|
|
|
139
139
|
}
|
|
140
140
|
return legacyFinish;
|
|
141
141
|
};
|
|
142
|
-
if (!hasRailing
|
|
142
|
+
if (!(hasRailing || hasTread || hasSide)) {
|
|
143
143
|
if (legacyFinish.material === undefined && legacyFinish.materialPreset === undefined) {
|
|
144
144
|
return node;
|
|
145
145
|
}
|
|
@@ -189,7 +189,7 @@ function migrateRoofSurfaceMaterials(node) {
|
|
|
189
189
|
material: node.material,
|
|
190
190
|
materialPreset: typeof node.materialPreset === 'string' ? node.materialPreset : undefined,
|
|
191
191
|
};
|
|
192
|
-
if (!hasTop
|
|
192
|
+
if (!(hasTop || hasEdge || hasWall)) {
|
|
193
193
|
if (legacyFinish.material === undefined && legacyFinish.materialPreset === undefined) {
|
|
194
194
|
return node;
|
|
195
195
|
}
|
|
@@ -290,6 +290,54 @@ function migrateNodes(nodes) {
|
|
|
290
290
|
}
|
|
291
291
|
return patchedNodes;
|
|
292
292
|
}
|
|
293
|
+
function getNodeChildIds(node) {
|
|
294
|
+
if (!('children' in node && Array.isArray(node.children))) {
|
|
295
|
+
return [];
|
|
296
|
+
}
|
|
297
|
+
return node.children
|
|
298
|
+
.map((child) => {
|
|
299
|
+
if (typeof child === 'string')
|
|
300
|
+
return child;
|
|
301
|
+
if (child && typeof child === 'object' && 'id' in child && typeof child.id === 'string') {
|
|
302
|
+
return child.id;
|
|
303
|
+
}
|
|
304
|
+
return null;
|
|
305
|
+
})
|
|
306
|
+
.filter((id) => typeof id === 'string');
|
|
307
|
+
}
|
|
308
|
+
function normalizeRootNodeIds(nodes, rootNodeIds) {
|
|
309
|
+
const existingRootIds = rootNodeIds.filter((id) => Boolean(nodes[id]));
|
|
310
|
+
const siteRootIds = existingRootIds.filter((id) => nodes[id]?.type === 'site');
|
|
311
|
+
if (siteRootIds.length > 0) {
|
|
312
|
+
return siteRootIds;
|
|
313
|
+
}
|
|
314
|
+
return existingRootIds.filter((id) => nodes[id]?.parentId === null);
|
|
315
|
+
}
|
|
316
|
+
function collectReachableNodeIds(nodes, rootNodeIds) {
|
|
317
|
+
const reachable = new Set();
|
|
318
|
+
const stack = [...rootNodeIds];
|
|
319
|
+
const childIdsByParentId = new Map();
|
|
320
|
+
for (const node of Object.values(nodes)) {
|
|
321
|
+
if (!node.parentId)
|
|
322
|
+
continue;
|
|
323
|
+
const parentId = node.parentId;
|
|
324
|
+
const children = childIdsByParentId.get(parentId) ?? [];
|
|
325
|
+
children.push(node.id);
|
|
326
|
+
childIdsByParentId.set(parentId, children);
|
|
327
|
+
}
|
|
328
|
+
while (stack.length > 0) {
|
|
329
|
+
const id = stack.pop();
|
|
330
|
+
if (!id || reachable.has(id))
|
|
331
|
+
continue;
|
|
332
|
+
const node = nodes[id];
|
|
333
|
+
if (!node)
|
|
334
|
+
continue;
|
|
335
|
+
reachable.add(id);
|
|
336
|
+
stack.push(...getNodeChildIds(node));
|
|
337
|
+
stack.push(...(childIdsByParentId.get(id) ?? []));
|
|
338
|
+
}
|
|
339
|
+
return reachable;
|
|
340
|
+
}
|
|
293
341
|
const useScene = create()(temporal((set, get) => ({
|
|
294
342
|
// 1. Flat dictionary of all nodes
|
|
295
343
|
nodes: {},
|
|
@@ -331,6 +379,22 @@ const useScene = create()(temporal((set, get) => ({
|
|
|
331
379
|
dirtyNodes: new Set(),
|
|
332
380
|
collections: {},
|
|
333
381
|
});
|
|
382
|
+
const normalizedRootNodeIds = normalizeRootNodeIds(cleanedNodes, rootNodeIds);
|
|
383
|
+
const reachableNodeIds = collectReachableNodeIds(cleanedNodes, normalizedRootNodeIds);
|
|
384
|
+
if (normalizedRootNodeIds.length > 0) {
|
|
385
|
+
for (const node of Object.values(cleanedNodes)) {
|
|
386
|
+
if (reachableNodeIds.has(node.id))
|
|
387
|
+
continue;
|
|
388
|
+
console.warn('[Scene] Removing unreachable node', node.id);
|
|
389
|
+
delete cleanedNodes[node.id];
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
set({
|
|
393
|
+
nodes: cleanedNodes,
|
|
394
|
+
rootNodeIds: normalizedRootNodeIds,
|
|
395
|
+
dirtyNodes: new Set(),
|
|
396
|
+
collections: {},
|
|
397
|
+
});
|
|
334
398
|
// Mark all nodes as dirty to trigger re-validation
|
|
335
399
|
Object.values(cleanedNodes).forEach((node) => {
|
|
336
400
|
get().markDirty(node.id);
|
|
@@ -489,8 +553,8 @@ let prevPastLength = 0;
|
|
|
489
553
|
let prevFutureLength = 0;
|
|
490
554
|
let prevNodesSnapshot = null;
|
|
491
555
|
export function clearSceneHistory() {
|
|
492
|
-
useScene.temporal.getState().clear();
|
|
493
556
|
resetSceneHistoryPauseDepth();
|
|
557
|
+
useScene.temporal.getState().clear();
|
|
494
558
|
prevPastLength = 0;
|
|
495
559
|
prevFutureLength = 0;
|
|
496
560
|
prevNodesSnapshot = null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stair-opening-sync.d.ts","sourceRoot":"","sources":["../../../src/systems/stair/stair-opening-sync.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"stair-opening-sync.d.ts","sourceRoot":"","sources":["../../../src/systems/stair/stair-opening-sync.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,OAAO,EACP,SAAS,EACT,WAAW,EACX,QAAQ,EAGT,MAAM,cAAc,CAAA;AA6pBrB,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;QAQvC,SAAS;UAAQ,OAAO,CAAC,QAAQ,GAAG,WAAW,CAAC;IAuG5E"}
|
|
@@ -29,7 +29,8 @@ function polygonsEqual(left, right) {
|
|
|
29
29
|
function metadataEqual(left, right) {
|
|
30
30
|
if (left.length !== right.length)
|
|
31
31
|
return false;
|
|
32
|
-
return left.every((entry, index) => entry.source === right[index]?.source &&
|
|
32
|
+
return left.every((entry, index) => entry.source === right[index]?.source &&
|
|
33
|
+
(entry.stairId ?? null) === (right[index]?.stairId ?? null));
|
|
33
34
|
}
|
|
34
35
|
function normalizeExistingMetadata(holes, metadata) {
|
|
35
36
|
return holes.map((_, index) => metadata?.[index] ?? { source: 'manual' });
|
|
@@ -185,12 +186,41 @@ function polygonArea(points) {
|
|
|
185
186
|
for (let index = 0; index < points.length; index += 1) {
|
|
186
187
|
const current = points[index];
|
|
187
188
|
const next = points[(index + 1) % points.length];
|
|
188
|
-
if (!current
|
|
189
|
+
if (!(current && next))
|
|
189
190
|
continue;
|
|
190
191
|
area += current[0] * next[1] - next[0] * current[1];
|
|
191
192
|
}
|
|
192
193
|
return area / 2;
|
|
193
194
|
}
|
|
195
|
+
function pointOnSegment(point, a, b, tolerance = 1e-6) {
|
|
196
|
+
const cross = (point[1] - a[1]) * (b[0] - a[0]) - (point[0] - a[0]) * (b[1] - a[1]);
|
|
197
|
+
if (Math.abs(cross) > tolerance)
|
|
198
|
+
return false;
|
|
199
|
+
const dot = (point[0] - a[0]) * (b[0] - a[0]) + (point[1] - a[1]) * (b[1] - a[1]);
|
|
200
|
+
if (dot < -tolerance)
|
|
201
|
+
return false;
|
|
202
|
+
const lenSq = (b[0] - a[0]) ** 2 + (b[1] - a[1]) ** 2;
|
|
203
|
+
return dot <= lenSq + tolerance;
|
|
204
|
+
}
|
|
205
|
+
function pointInPolygon(point, polygon) {
|
|
206
|
+
if (polygon.length < 3)
|
|
207
|
+
return false;
|
|
208
|
+
let inside = false;
|
|
209
|
+
const [x, z] = point;
|
|
210
|
+
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
|
211
|
+
const a = polygon[i];
|
|
212
|
+
const b = polygon[j];
|
|
213
|
+
if (pointOnSegment(point, a, b))
|
|
214
|
+
return true;
|
|
215
|
+
const intersects = a[1] > z !== b[1] > z && x < ((b[0] - a[0]) * (z - a[1])) / (b[1] - a[1]) + a[0];
|
|
216
|
+
if (intersects)
|
|
217
|
+
inside = !inside;
|
|
218
|
+
}
|
|
219
|
+
return inside;
|
|
220
|
+
}
|
|
221
|
+
function polygonContainsPolygon(outer, inner) {
|
|
222
|
+
return inner.every((point) => pointInPolygon(point, outer));
|
|
223
|
+
}
|
|
194
224
|
function getAxisAlignedRectFromPolygon(polygon) {
|
|
195
225
|
if (polygon.length < 4)
|
|
196
226
|
return null;
|
|
@@ -412,7 +442,9 @@ function getTargetCeilingElevationForStair(stair, ceiling, ceilingLevelId, nodes
|
|
|
412
442
|
if (fromLevel === undefined || ceilingLevel === undefined) {
|
|
413
443
|
return ceiling.height ?? DEFAULT_WALL_HEIGHT;
|
|
414
444
|
}
|
|
415
|
-
return (ceilingLevel - fromLevel) * DEFAULT_WALL_HEIGHT +
|
|
445
|
+
return ((ceilingLevel - fromLevel) * DEFAULT_WALL_HEIGHT +
|
|
446
|
+
(ceiling.height ?? DEFAULT_WALL_HEIGHT) -
|
|
447
|
+
(stair.position[1] ?? 0));
|
|
416
448
|
}
|
|
417
449
|
function shouldApplyStairToSlab(stair, slabLevelId, nodes) {
|
|
418
450
|
const { fromLevelId, toLevelId } = getResolvedStairLevelIds(stair, nodes);
|
|
@@ -467,10 +499,11 @@ export function syncAutoStairOpenings(nodes) {
|
|
|
467
499
|
source: 'stair',
|
|
468
500
|
stairId: stair.id,
|
|
469
501
|
},
|
|
470
|
-
})))
|
|
502
|
+
})))
|
|
503
|
+
.filter((hole) => polygonContainsPolygon(slab.polygon, hole.polygon));
|
|
471
504
|
const nextHoles = [...manualHoles, ...stairHoles.map((hole) => hole.polygon)];
|
|
472
505
|
const nextMetadata = [...manualMetadata, ...stairHoles.map((hole) => hole.metadata)];
|
|
473
|
-
if (!polygonsEqual(existingHoles, nextHoles)
|
|
506
|
+
if (!(polygonsEqual(existingHoles, nextHoles) && metadataEqual(existingMetadata, nextMetadata))) {
|
|
474
507
|
updates.push({
|
|
475
508
|
id: slab.id,
|
|
476
509
|
data: {
|
|
@@ -498,10 +531,11 @@ export function syncAutoStairOpenings(nodes) {
|
|
|
498
531
|
source: 'stair',
|
|
499
532
|
stairId: stair.id,
|
|
500
533
|
},
|
|
501
|
-
})))
|
|
534
|
+
})))
|
|
535
|
+
.filter((hole) => polygonContainsPolygon(ceiling.polygon, hole.polygon));
|
|
502
536
|
const nextHoles = [...manualHoles, ...stairHoles.map((hole) => hole.polygon)];
|
|
503
537
|
const nextMetadata = [...manualMetadata, ...stairHoles.map((hole) => hole.metadata)];
|
|
504
|
-
if (!polygonsEqual(existingHoles, nextHoles)
|
|
538
|
+
if (!(polygonsEqual(existingHoles, nextHoles) && metadataEqual(existingMetadata, nextMetadata))) {
|
|
505
539
|
updates.push({
|
|
506
540
|
id: ceiling.id,
|
|
507
541
|
data: {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stair-opening-sync.test.d.ts","sourceRoot":"","sources":["../../../src/systems/stair/stair-opening-sync.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import { BuildingNode, LevelNode, SlabNode, StairNode, StairSegmentNode } from '../../schema';
|
|
3
|
+
import { syncAutoStairOpenings } from './stair-opening-sync';
|
|
4
|
+
describe('syncAutoStairOpenings', () => {
|
|
5
|
+
test('only applies stair holes to destination slabs that contain the opening', () => {
|
|
6
|
+
const building = BuildingNode.parse({ name: 'Building' });
|
|
7
|
+
const ground = LevelNode.parse({ name: 'Ground', level: 0, parentId: building.id });
|
|
8
|
+
const upper = LevelNode.parse({ name: 'Upper', level: 1, parentId: building.id });
|
|
9
|
+
const landingSlab = SlabNode.parse({
|
|
10
|
+
name: 'Landing Slab',
|
|
11
|
+
parentId: upper.id,
|
|
12
|
+
polygon: [
|
|
13
|
+
[0, 0],
|
|
14
|
+
[4, 0],
|
|
15
|
+
[4, 3],
|
|
16
|
+
[0, 3],
|
|
17
|
+
],
|
|
18
|
+
});
|
|
19
|
+
const bedroomSlab = SlabNode.parse({
|
|
20
|
+
name: 'Bedroom Slab',
|
|
21
|
+
parentId: upper.id,
|
|
22
|
+
polygon: [
|
|
23
|
+
[4, 0],
|
|
24
|
+
[8, 0],
|
|
25
|
+
[8, 3],
|
|
26
|
+
[4, 3],
|
|
27
|
+
],
|
|
28
|
+
});
|
|
29
|
+
const segment = StairSegmentNode.parse({
|
|
30
|
+
parentId: 'stair_main',
|
|
31
|
+
width: 1,
|
|
32
|
+
length: 2.6,
|
|
33
|
+
height: 2.5,
|
|
34
|
+
stepCount: 12,
|
|
35
|
+
});
|
|
36
|
+
const stair = StairNode.parse({
|
|
37
|
+
id: 'stair_main',
|
|
38
|
+
name: 'Main Stair',
|
|
39
|
+
parentId: ground.id,
|
|
40
|
+
position: [2, 0, 0.2],
|
|
41
|
+
stairType: 'straight',
|
|
42
|
+
fromLevelId: ground.id,
|
|
43
|
+
toLevelId: upper.id,
|
|
44
|
+
slabOpeningMode: 'destination',
|
|
45
|
+
children: [segment.id],
|
|
46
|
+
});
|
|
47
|
+
const nodes = Object.fromEntries([
|
|
48
|
+
building,
|
|
49
|
+
ground,
|
|
50
|
+
upper,
|
|
51
|
+
landingSlab,
|
|
52
|
+
bedroomSlab,
|
|
53
|
+
stair,
|
|
54
|
+
{ ...segment, parentId: stair.id },
|
|
55
|
+
].map((node) => [node.id, node]));
|
|
56
|
+
const updates = syncAutoStairOpenings(nodes);
|
|
57
|
+
const landingUpdate = updates.find((update) => update.id === landingSlab.id);
|
|
58
|
+
const bedroomUpdate = updates.find((update) => update.id === bedroomSlab.id);
|
|
59
|
+
expect(landingUpdate?.data.holes).toHaveLength(1);
|
|
60
|
+
expect(landingUpdate?.data.holeMetadata).toEqual([{ source: 'stair', stairId: stair.id }]);
|
|
61
|
+
expect(bedroomUpdate).toBeUndefined();
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Point2D } from './wall-mitering';
|
|
2
1
|
import type { FenceNode, WallNode } from '../../schema';
|
|
2
|
+
import type { Point2D } from './wall-mitering';
|
|
3
3
|
type WallCurveLike = Pick<WallNode | FenceNode, 'start' | 'end' | 'curveOffset'>;
|
|
4
4
|
type CurveFrame = {
|
|
5
5
|
point: Point2D;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wall-curve.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-curve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"wall-curve.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-curve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAK9C,KAAK,aAAa,GAAG,IAAI,CAAC,QAAQ,GAAG,SAAS,EAAE,OAAO,GAAG,KAAK,GAAG,aAAa,CAAC,CAAA;AAEhF,KAAK,UAAU,GAAG;IAChB,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,OAAO,CAAA;CAChB,CAAA;AAED,KAAK,yBAAyB,GAAG;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAcD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAE9D;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAE5D;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,aAAa,UAErD;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,aAAa,UAExD;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,aAAa,UAE5D;AAWD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,UAG3E;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,aAAa,UAI5D;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,aAAa,WAE/C;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa;;;;;;;;;;;;;EA6BpD;AA+BD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,aAAa,EAAE,CAAC,EAAE,MAAM,GAAG,UAAU,CAwC9E;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,aAAa,WAE7D;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,aAAa,EAAE,QAAQ,SAA0B,aAM3F;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,aAAa,EAAE,QAAQ,SAA0B,UASzF;AAED,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,IAAI,CAAC,QAAQ,GAAG,SAAS,EAAE,OAAO,GAAG,KAAK,GAAG,aAAa,GAAG,WAAW,CAAC,EAC/E,QAAQ,SAA0B,EAClC,cAAc,CAAC,EAAE,yBAAyB,aA2B3C"}
|
|
@@ -78,7 +78,7 @@ function getWallArcData(wall) {
|
|
|
78
78
|
return null;
|
|
79
79
|
}
|
|
80
80
|
const absSagitta = Math.abs(sagitta);
|
|
81
|
-
const radius = chord.length * chord.length / (8 * absSagitta) + absSagitta / 2;
|
|
81
|
+
const radius = (chord.length * chord.length) / (8 * absSagitta) + absSagitta / 2;
|
|
82
82
|
const centerOffset = radius - absSagitta;
|
|
83
83
|
const direction = Math.sign(sagitta) || 1;
|
|
84
84
|
const center = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wall-mitering.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-mitering.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAO5C,MAAM,WAAW,OAAO;IACtB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;CACV;AAED,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,OAAO,CAAA;IAClB,UAAU,EAAE,OAAO,CAAA;IACnB,OAAO,EAAE,OAAO,CAAA;IAChB,QAAQ,EAAE,OAAO,CAAA;CAClB;AASD,KAAK,iBAAiB,GAAG,GAAG,CAAC,MAAM,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAAA;AAGzE,KAAK,YAAY,GAAG,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAA;AAQlD,iBAAS,UAAU,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,SAAY,GAAG,MAAM,CAG7D;AA8CD,UAAU,QAAQ;IAChB,YAAY,EAAE,OAAO,CAAA;IACrB,cAAc,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,OAAO,EAAE,OAAO,GAAG,KAAK,GAAG,aAAa,CAAA;KAAE,CAAC,CAAA;CACpF;
|
|
1
|
+
{"version":3,"file":"wall-mitering.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-mitering.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAO5C,MAAM,WAAW,OAAO;IACtB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;CACV;AAED,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,OAAO,CAAA;IAClB,UAAU,EAAE,OAAO,CAAA;IACnB,OAAO,EAAE,OAAO,CAAA;IAChB,QAAQ,EAAE,OAAO,CAAA;CAClB;AASD,KAAK,iBAAiB,GAAG,GAAG,CAAC,MAAM,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAAA;AAGzE,KAAK,YAAY,GAAG,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAA;AAQlD,iBAAS,UAAU,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,SAAY,GAAG,MAAM,CAG7D;AA8CD,UAAU,QAAQ;IAChB,YAAY,EAAE,OAAO,CAAA;IACrB,cAAc,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,OAAO,EAAE,OAAO,GAAG,KAAK,GAAG,aAAa,CAAA;KAAE,CAAC,CAAA;CACpF;AAoND,MAAM,WAAW,aAAa;IAE5B,YAAY,EAAE,YAAY,CAAA;IAE1B,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;CACjC;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,aAAa,CAWrE;AAED,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,QAAQ,EACd,SAAS,EAAE,aAAa,GACvB,uBAAuB,GAAG,IAAI,CA0BhC;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,CA8C/F;AAGD,OAAO,EAAE,UAAU,EAAE,CAAA"}
|
|
@@ -89,9 +89,7 @@ function getWallDirectionFromJunction(wall, endType) {
|
|
|
89
89
|
}
|
|
90
90
|
if (isCurvedWall(wall)) {
|
|
91
91
|
const frame = getWallCurveFrameAt(wall, endType === 'start' ? 0 : 1);
|
|
92
|
-
return endType === 'start'
|
|
93
|
-
? frame.tangent
|
|
94
|
-
: { x: -frame.tangent.x, y: -frame.tangent.y };
|
|
92
|
+
return endType === 'start' ? frame.tangent : { x: -frame.tangent.x, y: -frame.tangent.y };
|
|
95
93
|
}
|
|
96
94
|
return endType === 'start'
|
|
97
95
|
? { x: wall.end[0] - wall.start[0], y: wall.end[1] - wall.start[1] }
|
|
@@ -102,9 +100,7 @@ function getWallBoundaryFrame(wall, endType) {
|
|
|
102
100
|
const frame = getWallCurveFrameAt(wall, endType === 'start' ? 0 : 1);
|
|
103
101
|
return {
|
|
104
102
|
point: frame.point,
|
|
105
|
-
tangent: endType === 'start'
|
|
106
|
-
? frame.tangent
|
|
107
|
-
: { x: -frame.tangent.x, y: -frame.tangent.y },
|
|
103
|
+
tangent: endType === 'start' ? frame.tangent : { x: -frame.tangent.x, y: -frame.tangent.y },
|
|
108
104
|
normal: frame.normal,
|
|
109
105
|
};
|
|
110
106
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pascal-app/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Core library for Pascal 3D building editor",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -15,6 +15,36 @@
|
|
|
15
15
|
"types": "./dist/utils/clone-scene-graph.d.ts",
|
|
16
16
|
"import": "./dist/utils/clone-scene-graph.js",
|
|
17
17
|
"default": "./dist/utils/clone-scene-graph.js"
|
|
18
|
+
},
|
|
19
|
+
"./schema": {
|
|
20
|
+
"types": "./dist/schema/index.d.ts",
|
|
21
|
+
"import": "./dist/schema/index.js",
|
|
22
|
+
"default": "./dist/schema/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./store": {
|
|
25
|
+
"types": "./dist/store/use-scene.d.ts",
|
|
26
|
+
"import": "./dist/store/use-scene.js",
|
|
27
|
+
"default": "./dist/store/use-scene.js"
|
|
28
|
+
},
|
|
29
|
+
"./material-library": {
|
|
30
|
+
"types": "./dist/material-library.d.ts",
|
|
31
|
+
"import": "./dist/material-library.js",
|
|
32
|
+
"default": "./dist/material-library.js"
|
|
33
|
+
},
|
|
34
|
+
"./spatial-grid": {
|
|
35
|
+
"types": "./dist/hooks/spatial-grid/spatial-grid-manager.d.ts",
|
|
36
|
+
"import": "./dist/hooks/spatial-grid/spatial-grid-manager.js",
|
|
37
|
+
"default": "./dist/hooks/spatial-grid/spatial-grid-manager.js"
|
|
38
|
+
},
|
|
39
|
+
"./wall": {
|
|
40
|
+
"types": "./dist/systems/wall/wall-footprint.d.ts",
|
|
41
|
+
"import": "./dist/systems/wall/wall-footprint.js",
|
|
42
|
+
"default": "./dist/systems/wall/wall-footprint.js"
|
|
43
|
+
},
|
|
44
|
+
"./stair-openings": {
|
|
45
|
+
"types": "./dist/systems/stair/stair-opening-sync.d.ts",
|
|
46
|
+
"import": "./dist/systems/stair/stair-opening-sync.js",
|
|
47
|
+
"default": "./dist/systems/stair/stair-opening-sync.js"
|
|
18
48
|
}
|
|
19
49
|
},
|
|
20
50
|
"files": [
|
|
@@ -37,17 +67,16 @@
|
|
|
37
67
|
"idb-keyval": "^6.2.2",
|
|
38
68
|
"mitt": "^3.0.1",
|
|
39
69
|
"nanoid": "^5.1.6",
|
|
40
|
-
"three-bvh-csg": "^0.0.18",
|
|
41
|
-
"three-mesh-bvh": "^0.9.8",
|
|
42
70
|
"zod": "^4.3.5",
|
|
43
71
|
"zundo": "^2.3.0",
|
|
44
72
|
"zustand": "^5"
|
|
45
73
|
},
|
|
46
74
|
"devDependencies": {
|
|
47
75
|
"@pascal/typescript-config": "*",
|
|
76
|
+
"@types/bun": "^1.3.0",
|
|
48
77
|
"@types/react": "^19.2.2",
|
|
49
|
-
"
|
|
50
|
-
"
|
|
78
|
+
"@types/three": "^0.184.0",
|
|
79
|
+
"typescript": "6.0.2"
|
|
51
80
|
},
|
|
52
81
|
"keywords": [
|
|
53
82
|
"3d",
|
package/dist/materials.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { MeshStandardNodeMaterial } from 'three/webgpu';
|
|
2
|
-
/**
|
|
3
|
-
* Shared base material for structural elements: walls, frames, slabs, roof.
|
|
4
|
-
*/
|
|
5
|
-
export declare const baseMaterial: MeshStandardNodeMaterial;
|
|
6
|
-
/**
|
|
7
|
-
* Shared glass material for windows, glazed door panels, and glass items.
|
|
8
|
-
*/
|
|
9
|
-
export declare const glassMaterial: MeshStandardNodeMaterial;
|
|
10
|
-
//# sourceMappingURL=materials.d.ts.map
|
package/dist/materials.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"materials.d.ts","sourceRoot":"","sources":["../src/materials.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,wBAAwB,EAAE,MAAM,cAAc,CAAA;AAEnE;;GAEG;AACH,eAAO,MAAM,YAAY,0BAIvB,CAAA;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,0BASxB,CAAA"}
|
package/dist/materials.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { DoubleSide, MeshStandardNodeMaterial } from 'three/webgpu';
|
|
2
|
-
/**
|
|
3
|
-
* Shared base material for structural elements: walls, frames, slabs, roof.
|
|
4
|
-
*/
|
|
5
|
-
export const baseMaterial = new MeshStandardNodeMaterial({
|
|
6
|
-
color: '#f2f0ed',
|
|
7
|
-
roughness: 0.5,
|
|
8
|
-
metalness: 0,
|
|
9
|
-
});
|
|
10
|
-
/**
|
|
11
|
-
* Shared glass material for windows, glazed door panels, and glass items.
|
|
12
|
-
*/
|
|
13
|
-
export const glassMaterial = new MeshStandardNodeMaterial({
|
|
14
|
-
name: 'glass',
|
|
15
|
-
color: 'lightblue',
|
|
16
|
-
roughness: 0.05,
|
|
17
|
-
metalness: 0.1,
|
|
18
|
-
transparent: true,
|
|
19
|
-
opacity: 0.35,
|
|
20
|
-
side: DoubleSide,
|
|
21
|
-
depthWrite: false,
|
|
22
|
-
});
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import * as THREE from 'three';
|
|
2
|
-
import type { CeilingNode } from '../../schema';
|
|
3
|
-
export declare const CeilingSystem: () => null;
|
|
4
|
-
/**
|
|
5
|
-
* Generates flat ceiling geometry from polygon (no extrusion)
|
|
6
|
-
*/
|
|
7
|
-
export declare function generateCeilingGeometry(ceilingNode: CeilingNode): THREE.BufferGeometry;
|
|
8
|
-
//# sourceMappingURL=ceiling-system.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ceiling-system.d.ts","sourceRoot":"","sources":["../../../src/systems/ceiling/ceiling-system.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,EAAa,WAAW,EAAE,MAAM,cAAc,CAAA;AAc1D,eAAO,MAAM,aAAa,YAuBzB,CAAA;AAqBD;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,WAAW,GAAG,KAAK,CAAC,cAAc,CAgDtF"}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { useFrame } from '@react-three/fiber';
|
|
2
|
-
import * as THREE from 'three';
|
|
3
|
-
import { sceneRegistry } from '../../hooks/scene-registry/scene-registry';
|
|
4
|
-
import useScene from '../../store/use-scene';
|
|
5
|
-
function ensureUv2Attribute(geometry) {
|
|
6
|
-
const uv = geometry.getAttribute('uv');
|
|
7
|
-
if (!uv)
|
|
8
|
-
return;
|
|
9
|
-
geometry.setAttribute('uv2', new THREE.Float32BufferAttribute(Array.from(uv.array), 2));
|
|
10
|
-
}
|
|
11
|
-
// ============================================================================
|
|
12
|
-
// CEILING SYSTEM
|
|
13
|
-
// ============================================================================
|
|
14
|
-
export const CeilingSystem = () => {
|
|
15
|
-
const dirtyNodes = useScene((state) => state.dirtyNodes);
|
|
16
|
-
const clearDirty = useScene((state) => state.clearDirty);
|
|
17
|
-
useFrame(() => {
|
|
18
|
-
if (dirtyNodes.size === 0)
|
|
19
|
-
return;
|
|
20
|
-
const nodes = useScene.getState().nodes;
|
|
21
|
-
// Process dirty ceilings
|
|
22
|
-
dirtyNodes.forEach((id) => {
|
|
23
|
-
const node = nodes[id];
|
|
24
|
-
if (!node || node.type !== 'ceiling')
|
|
25
|
-
return;
|
|
26
|
-
const mesh = sceneRegistry.nodes.get(id);
|
|
27
|
-
if (mesh) {
|
|
28
|
-
updateCeilingGeometry(node, mesh);
|
|
29
|
-
clearDirty(id);
|
|
30
|
-
}
|
|
31
|
-
// If mesh not found, keep it dirty for next frame
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
return null;
|
|
35
|
-
};
|
|
36
|
-
/**
|
|
37
|
-
* Updates the geometry for a single ceiling
|
|
38
|
-
*/
|
|
39
|
-
function updateCeilingGeometry(node, mesh) {
|
|
40
|
-
const newGeo = generateCeilingGeometry(node);
|
|
41
|
-
mesh.geometry.dispose();
|
|
42
|
-
mesh.geometry = newGeo;
|
|
43
|
-
const gridMesh = mesh.getObjectByName('ceiling-grid');
|
|
44
|
-
if (gridMesh) {
|
|
45
|
-
gridMesh.geometry.dispose();
|
|
46
|
-
gridMesh.geometry = newGeo;
|
|
47
|
-
}
|
|
48
|
-
// Position at the ceiling height
|
|
49
|
-
mesh.position.y = (node.height ?? 2.5) - 0.01; // Slight offset to avoid z-fighting with upper-level slabs
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Generates flat ceiling geometry from polygon (no extrusion)
|
|
53
|
-
*/
|
|
54
|
-
export function generateCeilingGeometry(ceilingNode) {
|
|
55
|
-
const polygon = ceilingNode.polygon;
|
|
56
|
-
if (polygon.length < 3) {
|
|
57
|
-
return new THREE.BufferGeometry();
|
|
58
|
-
}
|
|
59
|
-
// Create shape from polygon
|
|
60
|
-
// Shape is in X-Y plane, we'll rotate to X-Z plane
|
|
61
|
-
const shape = new THREE.Shape();
|
|
62
|
-
const firstPt = polygon[0];
|
|
63
|
-
// Negate Y (which becomes Z) to get correct orientation after rotation
|
|
64
|
-
shape.moveTo(firstPt[0], -firstPt[1]);
|
|
65
|
-
for (let i = 1; i < polygon.length; i++) {
|
|
66
|
-
const pt = polygon[i];
|
|
67
|
-
shape.lineTo(pt[0], -pt[1]);
|
|
68
|
-
}
|
|
69
|
-
shape.closePath();
|
|
70
|
-
// Add holes to the shape
|
|
71
|
-
const holes = ceilingNode.holes || [];
|
|
72
|
-
for (const holePolygon of holes) {
|
|
73
|
-
if (holePolygon.length < 3)
|
|
74
|
-
continue;
|
|
75
|
-
const holePath = new THREE.Path();
|
|
76
|
-
const holeFirstPt = holePolygon[0];
|
|
77
|
-
holePath.moveTo(holeFirstPt[0], -holeFirstPt[1]);
|
|
78
|
-
for (let i = 1; i < holePolygon.length; i++) {
|
|
79
|
-
const pt = holePolygon[i];
|
|
80
|
-
holePath.lineTo(pt[0], -pt[1]);
|
|
81
|
-
}
|
|
82
|
-
holePath.closePath();
|
|
83
|
-
shape.holes.push(holePath);
|
|
84
|
-
}
|
|
85
|
-
// Create flat shape geometry (no extrusion)
|
|
86
|
-
const geometry = new THREE.ShapeGeometry(shape);
|
|
87
|
-
// Rotate so the shape lies flat in X-Z plane
|
|
88
|
-
geometry.rotateX(-Math.PI / 2);
|
|
89
|
-
geometry.computeVertexNormals();
|
|
90
|
-
ensureUv2Attribute(geometry);
|
|
91
|
-
return geometry;
|
|
92
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"door-system.d.ts","sourceRoot":"","sources":["../../../src/systems/door/door-system.tsx"],"names":[],"mappings":"AAUA,eAAO,MAAM,UAAU,YA2BtB,CAAA"}
|