@pascal-app/core 0.1.12 → 0.2.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 +22 -2
- package/dist/events/bus.d.ts.map +1 -1
- package/dist/hooks/scene-registry/scene-registry.d.ts +6 -1
- package/dist/hooks/scene-registry/scene-registry.d.ts.map +1 -1
- package/dist/hooks/scene-registry/scene-registry.js +11 -1
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts +17 -12
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-manager.js +135 -38
- package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-sync.js +16 -8
- package/dist/hooks/spatial-grid/spatial-grid.d.ts +3 -3
- package/dist/hooks/spatial-grid/spatial-grid.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid.js +2 -2
- package/dist/hooks/spatial-grid/use-spatial-query.d.ts +5 -0
- package/dist/hooks/spatial-grid/use-spatial-query.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/wall-spatial-grid.d.ts +6 -3
- package/dist/hooks/spatial-grid/wall-spatial-grid.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/wall-spatial-grid.js +35 -10
- package/dist/index.d.ts +8 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -6
- package/dist/lib/space-detection.d.ts.map +1 -1
- package/dist/lib/space-detection.js +1 -1
- package/dist/schema/collections.d.ts +11 -0
- package/dist/schema/collections.d.ts.map +1 -0
- package/dist/schema/collections.js +2 -0
- package/dist/schema/index.d.ts +11 -7
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +11 -6
- package/dist/schema/nodes/ceiling.d.ts +1 -0
- package/dist/schema/nodes/ceiling.d.ts.map +1 -1
- package/dist/schema/nodes/ceiling.js +2 -0
- package/dist/schema/nodes/door.d.ts +78 -0
- package/dist/schema/nodes/door.d.ts.map +1 -0
- package/dist/schema/nodes/door.js +67 -0
- package/dist/schema/nodes/item.d.ts +245 -2
- package/dist/schema/nodes/item.d.ts.map +1 -1
- package/dist/schema/nodes/item.js +72 -0
- package/dist/schema/nodes/level.d.ts.map +1 -1
- package/dist/schema/nodes/level.js +11 -1
- package/dist/schema/nodes/roof-segment.d.ts +51 -0
- package/dist/schema/nodes/roof-segment.d.ts.map +1 -0
- package/dist/schema/nodes/roof-segment.js +36 -0
- package/dist/schema/nodes/roof.d.ts +1 -4
- package/dist/schema/nodes/roof.d.ts.map +1 -1
- package/dist/schema/nodes/roof.js +9 -16
- package/dist/schema/nodes/site.d.ts +52 -1
- package/dist/schema/nodes/site.d.ts.map +1 -1
- package/dist/schema/nodes/slab.d.ts +1 -0
- package/dist/schema/nodes/slab.d.ts.map +1 -1
- package/dist/schema/nodes/slab.js +1 -0
- package/dist/schema/nodes/window.d.ts +40 -0
- package/dist/schema/nodes/window.d.ts.map +1 -0
- package/dist/schema/nodes/window.js +38 -0
- package/dist/schema/types.d.ts +241 -6
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/types.js +6 -0
- package/dist/store/actions/node-actions.d.ts.map +1 -1
- package/dist/store/actions/node-actions.js +23 -4
- package/dist/store/use-interactive.d.ts +18 -0
- package/dist/store/use-interactive.d.ts.map +1 -0
- package/dist/store/use-interactive.js +50 -0
- package/dist/store/use-scene.d.ts +10 -1
- package/dist/store/use-scene.d.ts.map +1 -1
- package/dist/store/use-scene.js +180 -57
- package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
- package/dist/systems/ceiling/ceiling-system.js +20 -0
- package/dist/systems/door/door-system.d.ts +2 -0
- package/dist/systems/door/door-system.d.ts.map +1 -0
- package/dist/systems/door/door-system.js +211 -0
- package/dist/systems/item/item-system.d.ts.map +1 -1
- package/dist/systems/item/item-system.js +10 -5
- package/dist/systems/roof/roof-system.d.ts +11 -3
- package/dist/systems/roof/roof-system.d.ts.map +1 -1
- package/dist/systems/roof/roof-system.js +705 -210
- package/dist/systems/slab/slab-system.d.ts.map +1 -1
- package/dist/systems/slab/slab-system.js +18 -3
- package/dist/systems/wall/wall-mitering.js +2 -2
- package/dist/systems/wall/wall-system.d.ts.map +1 -1
- package/dist/systems/wall/wall-system.js +11 -13
- package/dist/systems/window/window-system.d.ts +2 -0
- package/dist/systems/window/window-system.d.ts.map +1 -0
- package/dist/systems/window/window-system.js +147 -0
- package/package.json +10 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slab-system.d.ts","sourceRoot":"","sources":["../../../src/systems/slab/slab-system.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,EAAa,QAAQ,EAAE,MAAM,cAAc,CAAA;AAOvD,eAAO,MAAM,UAAU,YAwBtB,CAAA;AAkED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,GAAG,KAAK,CAAC,cAAc,
|
|
1
|
+
{"version":3,"file":"slab-system.d.ts","sourceRoot":"","sources":["../../../src/systems/slab/slab-system.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,EAAa,QAAQ,EAAE,MAAM,cAAc,CAAA;AAOvD,eAAO,MAAM,UAAU,YAwBtB,CAAA;AAkED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,GAAG,KAAK,CAAC,cAAc,CAmD7E"}
|
|
@@ -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
|
|
@@ -105,6 +105,21 @@ export function generateSlabGeometry(slabNode) {
|
|
|
105
105
|
shape.lineTo(pt[0], -pt[1]);
|
|
106
106
|
}
|
|
107
107
|
shape.closePath();
|
|
108
|
+
// Add holes to the shape
|
|
109
|
+
const holes = slabNode.holes || [];
|
|
110
|
+
for (const holePolygon of holes) {
|
|
111
|
+
if (holePolygon.length < 3)
|
|
112
|
+
continue;
|
|
113
|
+
const holePath = new THREE.Path();
|
|
114
|
+
const holeFirstPt = holePolygon[0];
|
|
115
|
+
holePath.moveTo(holeFirstPt[0], -holeFirstPt[1]);
|
|
116
|
+
for (let i = 1; i < holePolygon.length; i++) {
|
|
117
|
+
const pt = holePolygon[i];
|
|
118
|
+
holePath.lineTo(pt[0], -pt[1]);
|
|
119
|
+
}
|
|
120
|
+
holePath.closePath();
|
|
121
|
+
shape.holes.push(holePath);
|
|
122
|
+
}
|
|
108
123
|
// Extrude the shape by elevation
|
|
109
124
|
const geometry = new THREE.ExtrudeGeometry(shape, {
|
|
110
125
|
depth: elevation,
|
|
@@ -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)
|
|
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)
|
|
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;
|
|
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;AA0DD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,QAAQ,EAClB,aAAa,EAAE,OAAO,EAAE,EACxB,SAAS,EAAE,aAAa,EACxB,aAAa,SAAI,oFA4IlB"}
|
|
@@ -1,7 +1,7 @@
|
|
|
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';
|
|
@@ -33,7 +33,7 @@ export const WallSystem = () => {
|
|
|
33
33
|
if (!dirtyWallsByLevel.has(levelId)) {
|
|
34
34
|
dirtyWallsByLevel.set(levelId, new Set());
|
|
35
35
|
}
|
|
36
|
-
dirtyWallsByLevel.get(levelId)
|
|
36
|
+
dirtyWallsByLevel.get(levelId)?.add(id);
|
|
37
37
|
});
|
|
38
38
|
// Process each level that has dirty walls
|
|
39
39
|
for (const [levelId, dirtyWallIds] of dirtyWallsByLevel) {
|
|
@@ -59,7 +59,7 @@ export const WallSystem = () => {
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
-
});
|
|
62
|
+
}, 4);
|
|
63
63
|
return null;
|
|
64
64
|
};
|
|
65
65
|
/**
|
|
@@ -106,7 +106,7 @@ function updateWallGeometry(wallId, miterData) {
|
|
|
106
106
|
collisionMesh.geometry.dispose();
|
|
107
107
|
collisionMesh.geometry = collisionGeo;
|
|
108
108
|
}
|
|
109
|
-
mesh.position.set(node.start[0],
|
|
109
|
+
mesh.position.set(node.start[0], slabElevation, node.start[1]);
|
|
110
110
|
const angle = Math.atan2(node.end[1] - node.start[1], node.end[0] - node.start[0]);
|
|
111
111
|
mesh.rotation.y = -angle;
|
|
112
112
|
}
|
|
@@ -120,8 +120,10 @@ export function generateExtrudedWall(wallNode, childrenNodes, miterData, slabEle
|
|
|
120
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
|
-
//
|
|
124
|
-
|
|
123
|
+
// Positive slab: shift the whole wall up (full height preserved)
|
|
124
|
+
// Negative slab: extend wall downward so top stays fixed at wallNode.height
|
|
125
|
+
const wallHeight = wallNode.height ?? 2.5;
|
|
126
|
+
const height = slabElevation > 0 ? wallHeight : wallHeight - slabElevation;
|
|
125
127
|
const thickness = wallNode.thickness ?? 0.1;
|
|
126
128
|
const halfT = thickness / 2;
|
|
127
129
|
// Wall direction and normal (exactly like demo)
|
|
@@ -201,10 +203,6 @@ export function generateExtrudedWall(wallNode, childrenNodes, miterData, slabEle
|
|
|
201
203
|
});
|
|
202
204
|
// Rotate so extrusion direction (Z) becomes height direction (Y)
|
|
203
205
|
geometry.rotateX(-Math.PI / 2);
|
|
204
|
-
// Translate by slab elevation (works for both positive and negative values)
|
|
205
|
-
if (slabElevation !== 0) {
|
|
206
|
-
geometry.translate(0, slabElevation, 0);
|
|
207
|
-
}
|
|
208
206
|
geometry.computeVertexNormals();
|
|
209
207
|
// Apply CSG subtraction for cutouts (doors/windows)
|
|
210
208
|
const cutoutBrushes = collectCutoutBrushes(wallNode, childrenNodes, thickness);
|
|
@@ -249,7 +247,7 @@ function collectCutoutBrushes(wallNode, childrenNodes, wallThickness) {
|
|
|
249
247
|
wallMesh.updateMatrixWorld();
|
|
250
248
|
const wallMatrixInverse = wallMesh.matrixWorld.clone().invert();
|
|
251
249
|
for (const child of childrenNodes) {
|
|
252
|
-
if (child.type !== 'item')
|
|
250
|
+
if (child.type !== 'item' && child.type !== 'window' && child.type !== 'door')
|
|
253
251
|
continue;
|
|
254
252
|
const childMesh = sceneRegistry.nodes.get(child.id);
|
|
255
253
|
if (!childMesh)
|
|
@@ -264,8 +262,8 @@ function collectCutoutBrushes(wallNode, childrenNodes, wallThickness) {
|
|
|
264
262
|
continue;
|
|
265
263
|
// Calculate bounds in wall-local space
|
|
266
264
|
const v3 = new THREE.Vector3();
|
|
267
|
-
let minX =
|
|
268
|
-
let minY =
|
|
265
|
+
let minX = Number.POSITIVE_INFINITY, maxX = Number.NEGATIVE_INFINITY;
|
|
266
|
+
let minY = Number.POSITIVE_INFINITY, maxY = Number.NEGATIVE_INFINITY;
|
|
269
267
|
for (let i = 0; i < positions.count; i++) {
|
|
270
268
|
v3.fromBufferAttribute(positions, i);
|
|
271
269
|
v3.applyMatrix4(cutoutMesh.matrixWorld);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"window-system.d.ts","sourceRoot":"","sources":["../../../src/systems/window/window-system.tsx"],"names":[],"mappings":"AA4BA,eAAO,MAAM,YAAY,YA2BxB,CAAA"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { useFrame } from '@react-three/fiber';
|
|
2
|
+
import * as THREE from 'three';
|
|
3
|
+
import { DoubleSide, MeshStandardNodeMaterial } from 'three/webgpu';
|
|
4
|
+
import { sceneRegistry } from '../../hooks/scene-registry/scene-registry';
|
|
5
|
+
import useScene from '../../store/use-scene';
|
|
6
|
+
const glassMaterial = new MeshStandardNodeMaterial({
|
|
7
|
+
name: 'glass',
|
|
8
|
+
color: 'lightblue',
|
|
9
|
+
roughness: 0.05,
|
|
10
|
+
metalness: 0.1,
|
|
11
|
+
transparent: true,
|
|
12
|
+
opacity: 0.3,
|
|
13
|
+
side: DoubleSide,
|
|
14
|
+
depthWrite: false,
|
|
15
|
+
});
|
|
16
|
+
const frameMaterial = new MeshStandardNodeMaterial({
|
|
17
|
+
name: 'window-frame',
|
|
18
|
+
color: '#e8e8e8',
|
|
19
|
+
roughness: 0.6,
|
|
20
|
+
metalness: 0,
|
|
21
|
+
});
|
|
22
|
+
// Invisible material for root mesh — used as selection hitbox only
|
|
23
|
+
const hitboxMaterial = new THREE.MeshBasicMaterial({ visible: false });
|
|
24
|
+
export const WindowSystem = () => {
|
|
25
|
+
const dirtyNodes = useScene((state) => state.dirtyNodes);
|
|
26
|
+
const clearDirty = useScene((state) => state.clearDirty);
|
|
27
|
+
useFrame(() => {
|
|
28
|
+
if (dirtyNodes.size === 0)
|
|
29
|
+
return;
|
|
30
|
+
const nodes = useScene.getState().nodes;
|
|
31
|
+
dirtyNodes.forEach((id) => {
|
|
32
|
+
const node = nodes[id];
|
|
33
|
+
if (!node || node.type !== 'window')
|
|
34
|
+
return;
|
|
35
|
+
const mesh = sceneRegistry.nodes.get(id);
|
|
36
|
+
if (!mesh)
|
|
37
|
+
return; // Keep dirty until mesh mounts
|
|
38
|
+
updateWindowMesh(node, mesh);
|
|
39
|
+
clearDirty(id);
|
|
40
|
+
// Rebuild the parent wall so its cutout reflects the updated window geometry
|
|
41
|
+
if (node.parentId) {
|
|
42
|
+
useScene.getState().dirtyNodes.add(node.parentId);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}, 3);
|
|
46
|
+
return null;
|
|
47
|
+
};
|
|
48
|
+
function addBox(parent, material, w, h, d, x, y, z) {
|
|
49
|
+
const m = new THREE.Mesh(new THREE.BoxGeometry(w, h, d), material);
|
|
50
|
+
m.position.set(x, y, z);
|
|
51
|
+
parent.add(m);
|
|
52
|
+
}
|
|
53
|
+
function updateWindowMesh(node, mesh) {
|
|
54
|
+
// Root mesh is an invisible hitbox; all visuals live in child meshes
|
|
55
|
+
mesh.geometry.dispose();
|
|
56
|
+
mesh.geometry = new THREE.BoxGeometry(node.width, node.height, node.frameDepth);
|
|
57
|
+
mesh.material = hitboxMaterial;
|
|
58
|
+
// Sync transform from node (React may lag behind the system by a frame during drag)
|
|
59
|
+
mesh.position.set(node.position[0], node.position[1], node.position[2]);
|
|
60
|
+
mesh.rotation.set(node.rotation[0], node.rotation[1], node.rotation[2]);
|
|
61
|
+
// Dispose and remove all old visual children; preserve 'cutout'
|
|
62
|
+
for (const child of [...mesh.children]) {
|
|
63
|
+
if (child.name === 'cutout')
|
|
64
|
+
continue;
|
|
65
|
+
if (child instanceof THREE.Mesh)
|
|
66
|
+
child.geometry.dispose();
|
|
67
|
+
mesh.remove(child);
|
|
68
|
+
}
|
|
69
|
+
const { width, height, frameDepth, frameThickness, columnRatios, rowRatios, columnDividerThickness, rowDividerThickness, sill, sillDepth, sillThickness, } = node;
|
|
70
|
+
const innerW = width - 2 * frameThickness;
|
|
71
|
+
const innerH = height - 2 * frameThickness;
|
|
72
|
+
// ── Frame members ──
|
|
73
|
+
// Top / bottom — full width
|
|
74
|
+
addBox(mesh, frameMaterial, width, frameThickness, frameDepth, 0, height / 2 - frameThickness / 2, 0);
|
|
75
|
+
addBox(mesh, frameMaterial, width, frameThickness, frameDepth, 0, -height / 2 + frameThickness / 2, 0);
|
|
76
|
+
// Left / right — inner height to avoid corner overlap
|
|
77
|
+
addBox(mesh, frameMaterial, frameThickness, innerH, frameDepth, -width / 2 + frameThickness / 2, 0, 0);
|
|
78
|
+
addBox(mesh, frameMaterial, frameThickness, innerH, frameDepth, width / 2 - frameThickness / 2, 0, 0);
|
|
79
|
+
// ── Pane grid ──
|
|
80
|
+
const numCols = columnRatios.length;
|
|
81
|
+
const numRows = rowRatios.length;
|
|
82
|
+
const usableW = innerW - (numCols - 1) * columnDividerThickness;
|
|
83
|
+
const usableH = innerH - (numRows - 1) * rowDividerThickness;
|
|
84
|
+
const colSum = columnRatios.reduce((a, b) => a + b, 0);
|
|
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);
|
|
88
|
+
// Compute column x-centers starting from left edge of inner area
|
|
89
|
+
const colXCenters = [];
|
|
90
|
+
let cx = -innerW / 2;
|
|
91
|
+
for (let c = 0; c < numCols; c++) {
|
|
92
|
+
colXCenters.push(cx + colWidths[c] / 2);
|
|
93
|
+
cx += colWidths[c];
|
|
94
|
+
if (c < numCols - 1)
|
|
95
|
+
cx += columnDividerThickness;
|
|
96
|
+
}
|
|
97
|
+
// Compute row y-centers starting from top edge of inner area (R1 = top)
|
|
98
|
+
const rowYCenters = [];
|
|
99
|
+
let cy = innerH / 2;
|
|
100
|
+
for (let r = 0; r < numRows; r++) {
|
|
101
|
+
rowYCenters.push(cy - rowHeights[r] / 2);
|
|
102
|
+
cy -= rowHeights[r];
|
|
103
|
+
if (r < numRows - 1)
|
|
104
|
+
cy -= rowDividerThickness;
|
|
105
|
+
}
|
|
106
|
+
// Column dividers — full inner height
|
|
107
|
+
cx = -innerW / 2;
|
|
108
|
+
for (let c = 0; c < numCols - 1; c++) {
|
|
109
|
+
cx += colWidths[c];
|
|
110
|
+
addBox(mesh, frameMaterial, columnDividerThickness, innerH, frameDepth, cx + columnDividerThickness / 2, 0, 0);
|
|
111
|
+
cx += columnDividerThickness;
|
|
112
|
+
}
|
|
113
|
+
// Row dividers — per column width, so they don't overlap column dividers (top to bottom)
|
|
114
|
+
cy = innerH / 2;
|
|
115
|
+
for (let r = 0; r < numRows - 1; r++) {
|
|
116
|
+
cy -= rowHeights[r];
|
|
117
|
+
const divY = cy - rowDividerThickness / 2;
|
|
118
|
+
for (let c = 0; c < numCols; c++) {
|
|
119
|
+
addBox(mesh, frameMaterial, colWidths[c], rowDividerThickness, frameDepth, colXCenters[c], divY, 0);
|
|
120
|
+
}
|
|
121
|
+
cy -= rowDividerThickness;
|
|
122
|
+
}
|
|
123
|
+
// Glass panes
|
|
124
|
+
const glassDepth = Math.max(0.004, frameDepth * 0.08);
|
|
125
|
+
for (let c = 0; c < numCols; c++) {
|
|
126
|
+
for (let r = 0; r < numRows; r++) {
|
|
127
|
+
addBox(mesh, glassMaterial, colWidths[c], rowHeights[r], glassDepth, colXCenters[c], rowYCenters[r], 0);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// ── Sill ──
|
|
131
|
+
if (sill) {
|
|
132
|
+
const sillW = width + sillDepth * 0.4; // slightly wider than frame
|
|
133
|
+
// Protrudes from the front face of the frame (+Z)
|
|
134
|
+
const sillZ = frameDepth / 2 + sillDepth / 2;
|
|
135
|
+
addBox(mesh, frameMaterial, sillW, sillThickness, sillDepth, 0, -height / 2 - sillThickness / 2, sillZ);
|
|
136
|
+
}
|
|
137
|
+
// ── Cutout (for wall CSG) — always full window dimensions, 1m deep ──
|
|
138
|
+
let cutout = mesh.getObjectByName('cutout');
|
|
139
|
+
if (!cutout) {
|
|
140
|
+
cutout = new THREE.Mesh();
|
|
141
|
+
cutout.name = 'cutout';
|
|
142
|
+
mesh.add(cutout);
|
|
143
|
+
}
|
|
144
|
+
cutout.geometry.dispose();
|
|
145
|
+
cutout.geometry = new THREE.BoxGeometry(node.width, node.height, 1.0);
|
|
146
|
+
cutout.visible = false;
|
|
147
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pascal-app/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.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.
|
|
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.
|
|
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
|
-
"@
|
|
42
|
+
"@pascal/typescript-config": "*",
|
|
43
43
|
"@types/react": "^19.2.2",
|
|
44
|
-
"typescript": "5.9.
|
|
45
|
-
"@types/three": "^0.
|
|
44
|
+
"typescript": "5.9.3",
|
|
45
|
+
"@types/three": "^0.183.0"
|
|
46
46
|
},
|
|
47
47
|
"keywords": [
|
|
48
48
|
"3d",
|
|
@@ -54,8 +54,10 @@
|
|
|
54
54
|
],
|
|
55
55
|
"repository": {
|
|
56
56
|
"type": "git",
|
|
57
|
-
"url": "https://github.com/
|
|
57
|
+
"url": "https://github.com/pascalorg/editor.git",
|
|
58
58
|
"directory": "packages/core"
|
|
59
59
|
},
|
|
60
|
-
"license": "MIT"
|
|
60
|
+
"license": "MIT",
|
|
61
|
+
"homepage": "https://github.com/pascalorg/editor/tree/main/packages/core#readme",
|
|
62
|
+
"bugs": "https://github.com/pascalorg/editor/issues"
|
|
61
63
|
}
|