@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.
Files changed (120) hide show
  1. package/dist/events/bus.d.ts +45 -6
  2. package/dist/events/bus.d.ts.map +1 -1
  3. package/dist/events/bus.js +1 -1
  4. package/dist/hooks/scene-registry/scene-registry.d.ts +2 -0
  5. package/dist/hooks/scene-registry/scene-registry.d.ts.map +1 -1
  6. package/dist/hooks/scene-registry/scene-registry.js +2 -0
  7. package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
  8. package/dist/hooks/spatial-grid/spatial-grid-manager.js +164 -6
  9. package/dist/index.d.ts +8 -15
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +7 -14
  12. package/dist/lib/door-operation.d.ts +7 -0
  13. package/dist/lib/door-operation.d.ts.map +1 -0
  14. package/dist/lib/door-operation.js +25 -0
  15. package/dist/lib/polygon-geometry.d.ts.map +1 -1
  16. package/dist/lib/slab-polygon.d.ts +3 -0
  17. package/dist/lib/slab-polygon.d.ts.map +1 -0
  18. package/dist/lib/slab-polygon.js +58 -0
  19. package/dist/lib/space-detection.d.ts.map +1 -1
  20. package/dist/lib/space-detection.js +10 -8
  21. package/dist/material-library.d.ts +5 -3
  22. package/dist/material-library.d.ts.map +1 -1
  23. package/dist/material-library.js +28 -32
  24. package/dist/schema/asset-url.d.ts +34 -0
  25. package/dist/schema/asset-url.d.ts.map +1 -0
  26. package/dist/schema/asset-url.js +79 -0
  27. package/dist/schema/asset-url.test.d.ts +2 -0
  28. package/dist/schema/asset-url.test.d.ts.map +1 -0
  29. package/dist/schema/asset-url.test.js +134 -0
  30. package/dist/schema/index.d.ts +7 -5
  31. package/dist/schema/index.d.ts.map +1 -1
  32. package/dist/schema/index.js +5 -3
  33. package/dist/schema/material.d.ts +1 -0
  34. package/dist/schema/material.d.ts.map +1 -1
  35. package/dist/schema/material.js +13 -11
  36. package/dist/schema/nodes/column.d.ts +520 -0
  37. package/dist/schema/nodes/column.d.ts.map +1 -0
  38. package/dist/schema/nodes/column.js +385 -0
  39. package/dist/schema/nodes/door.d.ts +72 -0
  40. package/dist/schema/nodes/door.d.ts.map +1 -1
  41. package/dist/schema/nodes/door.js +39 -2
  42. package/dist/schema/nodes/fence.d.ts +1 -1
  43. package/dist/schema/nodes/fence.js +2 -2
  44. package/dist/schema/nodes/guide.d.ts +17 -0
  45. package/dist/schema/nodes/guide.d.ts.map +1 -1
  46. package/dist/schema/nodes/guide.js +11 -1
  47. package/dist/schema/nodes/item.d.ts +20 -0
  48. package/dist/schema/nodes/item.d.ts.map +1 -1
  49. package/dist/schema/nodes/item.js +30 -1
  50. package/dist/schema/nodes/level.d.ts +1 -1
  51. package/dist/schema/nodes/level.d.ts.map +1 -1
  52. package/dist/schema/nodes/level.js +6 -0
  53. package/dist/schema/nodes/roof-segment.d.ts +2 -2
  54. package/dist/schema/nodes/roof.d.ts +2 -2
  55. package/dist/schema/nodes/roof.d.ts.map +1 -1
  56. package/dist/schema/nodes/roof.js +5 -5
  57. package/dist/schema/nodes/scan.d.ts.map +1 -1
  58. package/dist/schema/nodes/scan.js +2 -1
  59. package/dist/schema/nodes/site.d.ts +7 -0
  60. package/dist/schema/nodes/site.d.ts.map +1 -1
  61. package/dist/schema/nodes/spawn.d.ts +24 -0
  62. package/dist/schema/nodes/spawn.d.ts.map +1 -0
  63. package/dist/schema/nodes/spawn.js +8 -0
  64. package/dist/schema/nodes/stair.d.ts +6 -6
  65. package/dist/schema/nodes/stair.d.ts.map +1 -1
  66. package/dist/schema/nodes/stair.js +9 -7
  67. package/dist/schema/nodes/window.d.ts +55 -0
  68. package/dist/schema/nodes/window.d.ts.map +1 -1
  69. package/dist/schema/nodes/window.js +29 -0
  70. package/dist/schema/types.d.ts +320 -5
  71. package/dist/schema/types.d.ts.map +1 -1
  72. package/dist/schema/types.js +4 -0
  73. package/dist/store/actions/node-actions.d.ts.map +1 -1
  74. package/dist/store/actions/node-actions.js +13 -10
  75. package/dist/store/use-interactive.d.ts +43 -0
  76. package/dist/store/use-interactive.d.ts.map +1 -1
  77. package/dist/store/use-interactive.js +66 -0
  78. package/dist/store/use-scene.d.ts.map +1 -1
  79. package/dist/store/use-scene.js +69 -5
  80. package/dist/systems/stair/stair-opening-sync.d.ts.map +1 -1
  81. package/dist/systems/stair/stair-opening-sync.js +41 -7
  82. package/dist/systems/stair/stair-opening-sync.test.d.ts +2 -0
  83. package/dist/systems/stair/stair-opening-sync.test.d.ts.map +1 -0
  84. package/dist/systems/stair/stair-opening-sync.test.js +63 -0
  85. package/dist/systems/wall/wall-curve.d.ts +1 -1
  86. package/dist/systems/wall/wall-curve.d.ts.map +1 -1
  87. package/dist/systems/wall/wall-curve.js +1 -1
  88. package/dist/systems/wall/wall-mitering.d.ts.map +1 -1
  89. package/dist/systems/wall/wall-mitering.js +2 -6
  90. package/package.json +34 -5
  91. package/dist/materials.d.ts +0 -10
  92. package/dist/materials.d.ts.map +0 -1
  93. package/dist/materials.js +0 -22
  94. package/dist/systems/ceiling/ceiling-system.d.ts +0 -8
  95. package/dist/systems/ceiling/ceiling-system.d.ts.map +0 -1
  96. package/dist/systems/ceiling/ceiling-system.js +0 -92
  97. package/dist/systems/door/door-system.d.ts +0 -2
  98. package/dist/systems/door/door-system.d.ts.map +0 -1
  99. package/dist/systems/door/door-system.js +0 -195
  100. package/dist/systems/fence/fence-system.d.ts +0 -2
  101. package/dist/systems/fence/fence-system.d.ts.map +0 -1
  102. package/dist/systems/fence/fence-system.js +0 -187
  103. package/dist/systems/item/item-system.d.ts +0 -2
  104. package/dist/systems/item/item-system.d.ts.map +0 -1
  105. package/dist/systems/item/item-system.js +0 -48
  106. package/dist/systems/roof/roof-system.d.ts +0 -16
  107. package/dist/systems/roof/roof-system.d.ts.map +0 -1
  108. package/dist/systems/roof/roof-system.js +0 -797
  109. package/dist/systems/slab/slab-system.d.ts +0 -8
  110. package/dist/systems/slab/slab-system.d.ts.map +0 -1
  111. package/dist/systems/slab/slab-system.js +0 -214
  112. package/dist/systems/stair/stair-system.d.ts +0 -2
  113. package/dist/systems/stair/stair-system.d.ts.map +0 -1
  114. package/dist/systems/stair/stair-system.js +0 -776
  115. package/dist/systems/wall/wall-system.d.ts +0 -12
  116. package/dist/systems/wall/wall-system.d.ts.map +0 -1
  117. package/dist/systems/wall/wall-system.js +0 -455
  118. package/dist/systems/window/window-system.d.ts +0 -2
  119. package/dist/systems/window/window-system.d.ts.map +0 -1
  120. 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;AAiVzD,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,aA8Nf,CAAA;AAED,eAAe,QAAQ,CAAA;AAOvB,wBAAgB,iBAAiB,SAMhC"}
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"}
@@ -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 && !hasExterior) {
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 && !hasTread && !hasSide) {
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 && !hasEdge && !hasWall) {
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":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAa,QAAQ,EAA+B,MAAM,cAAc,CAAA;AAulBrH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;QAIvC,SAAS;UAAQ,OAAO,CAAC,QAAQ,GAAG,WAAW,CAAC;IA6F5E"}
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 && (entry.stairId ?? null) === (right[index]?.stairId ?? null));
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 || !next)
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 + (ceiling.height ?? DEFAULT_WALL_HEIGHT) - (stair.position[1] ?? 0);
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) || !metadataEqual(existingMetadata, nextMetadata)) {
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) || !metadataEqual(existingMetadata, nextMetadata)) {
538
+ if (!(polygonsEqual(existingHoles, nextHoles) && metadataEqual(existingMetadata, nextMetadata))) {
505
539
  updates.push({
506
540
  id: ceiling.id,
507
541
  data: {
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=stair-opening-sync.test.d.ts.map
@@ -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,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAKvD,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,aAG3F;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"}
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;AA+ND,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"}
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.6.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
- "typescript": "5.9.3",
50
- "@types/three": "^0.184.0"
78
+ "@types/three": "^0.184.0",
79
+ "typescript": "6.0.2"
51
80
  },
52
81
  "keywords": [
53
82
  "3d",
@@ -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
@@ -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,2 +0,0 @@
1
- export declare const DoorSystem: () => null;
2
- //# sourceMappingURL=door-system.d.ts.map
@@ -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"}