@pascal-app/core 0.1.13 → 0.3.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 (74) hide show
  1. package/dist/events/bus.d.ts +15 -2
  2. package/dist/events/bus.d.ts.map +1 -1
  3. package/dist/hooks/scene-registry/scene-registry.d.ts +5 -1
  4. package/dist/hooks/scene-registry/scene-registry.d.ts.map +1 -1
  5. package/dist/hooks/scene-registry/scene-registry.js +10 -1
  6. package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts +8 -8
  7. package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
  8. package/dist/hooks/spatial-grid/spatial-grid-manager.js +88 -36
  9. package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts +1 -1
  10. package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts.map +1 -1
  11. package/dist/hooks/spatial-grid/spatial-grid-sync.js +16 -8
  12. package/dist/hooks/spatial-grid/spatial-grid.d.ts +3 -3
  13. package/dist/hooks/spatial-grid/spatial-grid.d.ts.map +1 -1
  14. package/dist/hooks/spatial-grid/spatial-grid.js +2 -2
  15. package/dist/hooks/spatial-grid/use-spatial-query.d.ts.map +1 -1
  16. package/dist/hooks/spatial-grid/wall-spatial-grid.d.ts +2 -2
  17. package/dist/hooks/spatial-grid/wall-spatial-grid.d.ts.map +1 -1
  18. package/dist/hooks/spatial-grid/wall-spatial-grid.js +2 -2
  19. package/dist/index.d.ts +6 -2
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +5 -1
  22. package/dist/lib/space-detection.d.ts.map +1 -1
  23. package/dist/lib/space-detection.js +3 -1
  24. package/dist/schema/collections.d.ts +11 -0
  25. package/dist/schema/collections.d.ts.map +1 -0
  26. package/dist/schema/collections.js +2 -0
  27. package/dist/schema/index.d.ts +11 -8
  28. package/dist/schema/index.d.ts.map +1 -1
  29. package/dist/schema/index.js +11 -7
  30. package/dist/schema/nodes/door.d.ts +78 -0
  31. package/dist/schema/nodes/door.d.ts.map +1 -0
  32. package/dist/schema/nodes/door.js +67 -0
  33. package/dist/schema/nodes/item.d.ts +234 -0
  34. package/dist/schema/nodes/item.d.ts.map +1 -1
  35. package/dist/schema/nodes/item.js +65 -1
  36. package/dist/schema/nodes/level.d.ts.map +1 -1
  37. package/dist/schema/nodes/level.js +11 -1
  38. package/dist/schema/nodes/roof-segment.d.ts +51 -0
  39. package/dist/schema/nodes/roof-segment.d.ts.map +1 -0
  40. package/dist/schema/nodes/roof-segment.js +36 -0
  41. package/dist/schema/nodes/roof.d.ts +1 -4
  42. package/dist/schema/nodes/roof.d.ts.map +1 -1
  43. package/dist/schema/nodes/roof.js +9 -16
  44. package/dist/schema/nodes/site.d.ts +46 -0
  45. package/dist/schema/nodes/site.d.ts.map +1 -1
  46. package/dist/schema/types.d.ts +191 -4
  47. package/dist/schema/types.d.ts.map +1 -1
  48. package/dist/schema/types.js +4 -0
  49. package/dist/store/actions/node-actions.d.ts.map +1 -1
  50. package/dist/store/actions/node-actions.js +23 -4
  51. package/dist/store/use-interactive.d.ts +18 -0
  52. package/dist/store/use-interactive.d.ts.map +1 -0
  53. package/dist/store/use-interactive.js +50 -0
  54. package/dist/store/use-scene.d.ts +10 -1
  55. package/dist/store/use-scene.d.ts.map +1 -1
  56. package/dist/store/use-scene.js +190 -57
  57. package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
  58. package/dist/systems/ceiling/ceiling-system.js +5 -0
  59. package/dist/systems/door/door-system.d.ts +2 -0
  60. package/dist/systems/door/door-system.d.ts.map +1 -0
  61. package/dist/systems/door/door-system.js +211 -0
  62. package/dist/systems/item/item-system.js +3 -2
  63. package/dist/systems/roof/roof-system.d.ts +11 -3
  64. package/dist/systems/roof/roof-system.d.ts.map +1 -1
  65. package/dist/systems/roof/roof-system.js +705 -210
  66. package/dist/systems/slab/slab-system.js +3 -3
  67. package/dist/systems/wall/wall-footprint.d.ts +7 -0
  68. package/dist/systems/wall/wall-footprint.d.ts.map +1 -0
  69. package/dist/systems/wall/wall-footprint.js +49 -0
  70. package/dist/systems/wall/wall-mitering.js +2 -2
  71. package/dist/systems/wall/wall-system.d.ts.map +1 -1
  72. package/dist/systems/wall/wall-system.js +13 -50
  73. package/dist/systems/window/window-system.js +3 -3
  74. package/package.json +6 -6
@@ -24,7 +24,7 @@ export const SlabSystem = () => {
24
24
  }
25
25
  // If mesh not found, keep it dirty for next frame
26
26
  });
27
- });
27
+ }, 1);
28
28
  return null;
29
29
  };
30
30
  /**
@@ -63,8 +63,8 @@ function outsetPolygon(polygon, amount) {
63
63
  offEdges.push([polygon[i][0], polygon[i][1], dx, dz]);
64
64
  continue;
65
65
  }
66
- const nx = (s * dz / len) * amount;
67
- const nz = (s * -dx / len) * amount;
66
+ const nx = ((s * dz) / len) * amount;
67
+ const nz = ((s * -dx) / len) * amount;
68
68
  offEdges.push([polygon[i][0] + nx, polygon[i][1] + nz, dx, dz]);
69
69
  }
70
70
  // Intersect consecutive offset edges to get new vertices
@@ -0,0 +1,7 @@
1
+ import type { WallNode } from '../../schema';
2
+ import { type Point2D, type WallMiterData } from './wall-mitering';
3
+ export declare const DEFAULT_WALL_THICKNESS = 0.1;
4
+ export declare const DEFAULT_WALL_HEIGHT = 2.5;
5
+ export declare function getWallThickness(wallNode: WallNode): number;
6
+ export declare function getWallPlanFootprint(wallNode: WallNode, miterData: WallMiterData): Point2D[];
7
+ //# sourceMappingURL=wall-footprint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wall-footprint.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-footprint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,KAAK,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAE9E,eAAO,MAAM,sBAAsB,MAAM,CAAA;AACzC,eAAO,MAAM,mBAAmB,MAAM,CAAA;AAEtC,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAE3D;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,GAAG,OAAO,EAAE,CAkD5F"}
@@ -0,0 +1,49 @@
1
+ import { pointToKey } from './wall-mitering';
2
+ export const DEFAULT_WALL_THICKNESS = 0.1;
3
+ export const DEFAULT_WALL_HEIGHT = 2.5;
4
+ export function getWallThickness(wallNode) {
5
+ return wallNode.thickness ?? DEFAULT_WALL_THICKNESS;
6
+ }
7
+ export function getWallPlanFootprint(wallNode, miterData) {
8
+ const { junctionData } = miterData;
9
+ const wallStart = { x: wallNode.start[0], y: wallNode.start[1] };
10
+ const wallEnd = { x: wallNode.end[0], y: wallNode.end[1] };
11
+ const thickness = getWallThickness(wallNode);
12
+ const halfT = thickness / 2;
13
+ const v = { x: wallEnd.x - wallStart.x, y: wallEnd.y - wallStart.y };
14
+ const L = Math.sqrt(v.x * v.x + v.y * v.y);
15
+ if (L < 1e-9) {
16
+ return [];
17
+ }
18
+ const nUnit = { x: -v.y / L, y: v.x / L };
19
+ const keyStart = pointToKey(wallStart);
20
+ const keyEnd = pointToKey(wallEnd);
21
+ const startJunction = junctionData.get(keyStart)?.get(wallNode.id);
22
+ const endJunction = junctionData.get(keyEnd)?.get(wallNode.id);
23
+ const pStartLeft = startJunction?.left || {
24
+ x: wallStart.x + nUnit.x * halfT,
25
+ y: wallStart.y + nUnit.y * halfT,
26
+ };
27
+ const pStartRight = startJunction?.right || {
28
+ x: wallStart.x - nUnit.x * halfT,
29
+ y: wallStart.y - nUnit.y * halfT,
30
+ };
31
+ // Junction offsets are stored relative to the outgoing direction.
32
+ const pEndLeft = endJunction?.right || {
33
+ x: wallEnd.x + nUnit.x * halfT,
34
+ y: wallEnd.y + nUnit.y * halfT,
35
+ };
36
+ const pEndRight = endJunction?.left || {
37
+ x: wallEnd.x - nUnit.x * halfT,
38
+ y: wallEnd.y - nUnit.y * halfT,
39
+ };
40
+ const polygon = [pStartRight, pEndRight];
41
+ if (endJunction) {
42
+ polygon.push(wallEnd);
43
+ }
44
+ polygon.push(pEndLeft, pStartLeft);
45
+ if (startJunction) {
46
+ polygon.push(wallStart);
47
+ }
48
+ return polygon;
49
+ }
@@ -52,11 +52,11 @@ function findJunctions(walls) {
52
52
  if (!junctions.has(keyStart)) {
53
53
  junctions.set(keyStart, { meetingPoint: startPt, connectedWalls: [] });
54
54
  }
55
- junctions.get(keyStart).connectedWalls.push({ wall, endType: 'start' });
55
+ junctions.get(keyStart)?.connectedWalls.push({ wall, endType: 'start' });
56
56
  if (!junctions.has(keyEnd)) {
57
57
  junctions.set(keyEnd, { meetingPoint: endPt, connectedWalls: [] });
58
58
  }
59
- junctions.get(keyEnd).connectedWalls.push({ wall, endType: 'end' });
59
+ junctions.get(keyEnd)?.connectedWalls.push({ wall, endType: 'end' });
60
60
  }
61
61
  // Second pass: detect T-junctions (walls passing through junction points)
62
62
  for (const [_key, junction] of junctions.entries()) {
@@ -1 +1 @@
1
- {"version":3,"file":"wall-system.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-system.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAM9B,OAAO,KAAK,EAAE,OAAO,EAAa,QAAQ,EAAE,MAAM,cAAc,CAAA;AAEhE,OAAO,EAKL,KAAK,aAAa,EACnB,MAAM,iBAAiB,CAAA;AAUxB,eAAO,MAAM,UAAU,YAuDtB,CAAA;AA2DD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,QAAQ,EAClB,aAAa,EAAE,OAAO,EAAE,EACxB,SAAS,EAAE,aAAa,EACxB,aAAa,SAAI,oFA4IlB"}
1
+ {"version":3,"file":"wall-system.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-system.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAM9B,OAAO,KAAK,EAAE,OAAO,EAAa,QAAQ,EAAE,MAAM,cAAc,CAAA;AAGhE,OAAO,EAIL,KAAK,aAAa,EACnB,MAAM,iBAAiB,CAAA;AAUxB,eAAO,MAAM,UAAU,YAuDtB,CAAA;AA0DD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,QAAQ,EAClB,aAAa,EAAE,OAAO,EAAE,EACxB,SAAS,EAAE,aAAa,EACxB,aAAa,SAAI,oFA+FlB"}
@@ -1,12 +1,13 @@
1
1
  import { useFrame } from '@react-three/fiber';
2
2
  import * as THREE from 'three';
3
- import { computeBoundsTree } from 'three-mesh-bvh';
4
3
  import { Brush, Evaluator, SUBTRACTION } from 'three-bvh-csg';
4
+ import { computeBoundsTree } from 'three-mesh-bvh';
5
5
  import { sceneRegistry } from '../../hooks/scene-registry/scene-registry';
6
6
  import { spatialGridManager } from '../../hooks/spatial-grid/spatial-grid-manager';
7
7
  import { resolveLevelId } from '../../hooks/spatial-grid/spatial-grid-sync';
8
8
  import useScene from '../../store/use-scene';
9
- import { calculateLevelMiters, getAdjacentWallIds, pointToKey, } from './wall-mitering';
9
+ import { DEFAULT_WALL_HEIGHT, getWallPlanFootprint, getWallThickness } from './wall-footprint';
10
+ import { calculateLevelMiters, getAdjacentWallIds, } from './wall-mitering';
10
11
  // Reusable CSG evaluator for better performance
11
12
  const csgEvaluator = new Evaluator();
12
13
  // ============================================================================
@@ -33,7 +34,7 @@ export const WallSystem = () => {
33
34
  if (!dirtyWallsByLevel.has(levelId)) {
34
35
  dirtyWallsByLevel.set(levelId, new Set());
35
36
  }
36
- dirtyWallsByLevel.get(levelId).add(id);
37
+ dirtyWallsByLevel.get(levelId)?.add(id);
37
38
  });
38
39
  // Process each level that has dirty walls
39
40
  for (const [levelId, dirtyWallIds] of dirtyWallsByLevel) {
@@ -59,7 +60,7 @@ export const WallSystem = () => {
59
60
  }
60
61
  }
61
62
  }
62
- });
63
+ }, 4);
63
64
  return null;
64
65
  };
65
66
  /**
@@ -117,60 +118,22 @@ function updateWallGeometry(wallId, miterData) {
117
118
  * then we transform to wall-local for the 3D mesh.
118
119
  */
119
120
  export function generateExtrudedWall(wallNode, childrenNodes, miterData, slabElevation = 0) {
120
- const { junctionData } = miterData;
121
121
  const wallStart = { x: wallNode.start[0], y: wallNode.start[1] };
122
122
  const wallEnd = { x: wallNode.end[0], y: wallNode.end[1] };
123
123
  // Positive slab: shift the whole wall up (full height preserved)
124
124
  // Negative slab: extend wall downward so top stays fixed at wallNode.height
125
- const wallHeight = wallNode.height ?? 2.5;
125
+ const wallHeight = wallNode.height ?? DEFAULT_WALL_HEIGHT;
126
126
  const height = slabElevation > 0 ? wallHeight : wallHeight - slabElevation;
127
- const thickness = wallNode.thickness ?? 0.1;
128
- const halfT = thickness / 2;
127
+ const thickness = getWallThickness(wallNode);
129
128
  // Wall direction and normal (exactly like demo)
130
129
  const v = { x: wallEnd.x - wallStart.x, y: wallEnd.y - wallStart.y };
131
130
  const L = Math.sqrt(v.x * v.x + v.y * v.y);
132
131
  if (L < 1e-9) {
133
132
  return new THREE.BufferGeometry();
134
133
  }
135
- const nUnit = { x: -v.y / L, y: v.x / L };
136
- // Get junction data for start and end (exactly like demo)
137
- const keyStart = pointToKey(wallStart);
138
- const keyEnd = pointToKey(wallEnd);
139
- const startJunction = junctionData.get(keyStart)?.get(wallNode.id);
140
- const endJunction = junctionData.get(keyEnd)?.get(wallNode.id);
141
- // Calculate polygon corners in world coordinates (exactly like demo)
142
- // p_start_L = left side at start
143
- // p_start_R = right side at start
144
- // p_end_L = left side at end
145
- // p_end_R = right side at end
146
- const p_start_L = startJunction?.left || {
147
- x: wallStart.x + nUnit.x * halfT,
148
- y: wallStart.y + nUnit.y * halfT,
149
- };
150
- const p_start_R = startJunction?.right || {
151
- x: wallStart.x - nUnit.x * halfT,
152
- y: wallStart.y - nUnit.y * halfT,
153
- };
154
- // At end, SWAP left/right from junction data (exactly like demo)
155
- // This is because junction stores left/right relative to OUTGOING direction,
156
- // which is reversed at the end of the wall
157
- const p_end_L = endJunction?.right || {
158
- x: wallEnd.x + nUnit.x * halfT,
159
- y: wallEnd.y + nUnit.y * halfT,
160
- };
161
- const p_end_R = endJunction?.left || {
162
- x: wallEnd.x - nUnit.x * halfT,
163
- y: wallEnd.y - nUnit.y * halfT,
164
- };
165
- // Build polygon points (exactly like demo)
166
- // Order: start-right -> end-right -> [end center] -> end-left -> start-left -> [start center]
167
- const polyPoints = [p_start_R, p_end_R];
168
- if (endJunction) {
169
- polyPoints.push(wallEnd); // Add center vertex at junction
170
- }
171
- polyPoints.push(p_end_L, p_start_L);
172
- if (startJunction) {
173
- polyPoints.push(wallStart); // Add center vertex at junction
134
+ const polyPoints = getWallPlanFootprint(wallNode, miterData);
135
+ if (polyPoints.length < 3) {
136
+ return new THREE.BufferGeometry();
174
137
  }
175
138
  // Transform world coordinates to wall-local coordinates
176
139
  // Wall-local: x along wall, z perpendicular (thickness direction)
@@ -247,7 +210,7 @@ function collectCutoutBrushes(wallNode, childrenNodes, wallThickness) {
247
210
  wallMesh.updateMatrixWorld();
248
211
  const wallMatrixInverse = wallMesh.matrixWorld.clone().invert();
249
212
  for (const child of childrenNodes) {
250
- if (child.type !== 'item' && child.type !== 'window')
213
+ if (child.type !== 'item' && child.type !== 'window' && child.type !== 'door')
251
214
  continue;
252
215
  const childMesh = sceneRegistry.nodes.get(child.id);
253
216
  if (!childMesh)
@@ -262,8 +225,8 @@ function collectCutoutBrushes(wallNode, childrenNodes, wallThickness) {
262
225
  continue;
263
226
  // Calculate bounds in wall-local space
264
227
  const v3 = new THREE.Vector3();
265
- let minX = Infinity, maxX = -Infinity;
266
- let minY = Infinity, maxY = -Infinity;
228
+ let minX = Number.POSITIVE_INFINITY, maxX = Number.NEGATIVE_INFINITY;
229
+ let minY = Number.POSITIVE_INFINITY, maxY = Number.NEGATIVE_INFINITY;
267
230
  for (let i = 0; i < positions.count; i++) {
268
231
  v3.fromBufferAttribute(positions, i);
269
232
  v3.applyMatrix4(cutoutMesh.matrixWorld);
@@ -42,7 +42,7 @@ export const WindowSystem = () => {
42
42
  useScene.getState().dirtyNodes.add(node.parentId);
43
43
  }
44
44
  });
45
- });
45
+ }, 3);
46
46
  return null;
47
47
  };
48
48
  function addBox(parent, material, w, h, d, x, y, z) {
@@ -83,8 +83,8 @@ function updateWindowMesh(node, mesh) {
83
83
  const usableH = innerH - (numRows - 1) * rowDividerThickness;
84
84
  const colSum = columnRatios.reduce((a, b) => a + b, 0);
85
85
  const rowSum = rowRatios.reduce((a, b) => a + b, 0);
86
- const colWidths = columnRatios.map(r => (r / colSum) * usableW);
87
- const rowHeights = rowRatios.map(r => (r / rowSum) * usableH);
86
+ const colWidths = columnRatios.map((r) => (r / colSum) * usableW);
87
+ const rowHeights = rowRatios.map((r) => (r / rowSum) * usableH);
88
88
  // Compute column x-centers starting from left edge of inner area
89
89
  const colXCenters = [];
90
90
  let cx = -innerW / 2;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pascal-app/core",
3
- "version": "0.1.13",
3
+ "version": "0.3.0",
4
4
  "description": "Core library for Pascal 3D building editor",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -25,24 +25,24 @@
25
25
  "@react-three/drei": "^10",
26
26
  "@react-three/fiber": "^9",
27
27
  "react": "^18 || ^19",
28
- "three": "^0.182"
28
+ "three": "^0.183"
29
29
  },
30
30
  "dependencies": {
31
31
  "dedent": "^1.7.1",
32
32
  "idb-keyval": "^6.2.2",
33
33
  "mitt": "^3.0.1",
34
34
  "nanoid": "^5.1.6",
35
- "three-bvh-csg": "^0.0.17",
35
+ "three-bvh-csg": "^0.0.18",
36
36
  "three-mesh-bvh": "^0.9.8",
37
37
  "zod": "^4.3.5",
38
38
  "zundo": "^2.3.0",
39
39
  "zustand": "^5"
40
40
  },
41
41
  "devDependencies": {
42
- "@repo/typescript-config": "*",
42
+ "@pascal/typescript-config": "*",
43
43
  "@types/react": "^19.2.2",
44
- "typescript": "5.9.2",
45
- "@types/three": "^0.182.0"
44
+ "typescript": "5.9.3",
45
+ "@types/three": "^0.183.0"
46
46
  },
47
47
  "keywords": [
48
48
  "3d",