@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,195 +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 { baseMaterial, glassMaterial } from '../../materials';
5
- import useScene from '../../store/use-scene';
6
- // Invisible material for root mesh — used as selection hitbox only
7
- const hitboxMaterial = new THREE.MeshBasicMaterial({ visible: false });
8
- export const DoorSystem = () => {
9
- const dirtyNodes = useScene((state) => state.dirtyNodes);
10
- const clearDirty = useScene((state) => state.clearDirty);
11
- useFrame(() => {
12
- if (dirtyNodes.size === 0)
13
- return;
14
- const nodes = useScene.getState().nodes;
15
- dirtyNodes.forEach((id) => {
16
- const node = nodes[id];
17
- if (!node || node.type !== 'door')
18
- return;
19
- const mesh = sceneRegistry.nodes.get(id);
20
- if (!mesh)
21
- return; // Keep dirty until mesh mounts
22
- updateDoorMesh(node, mesh);
23
- clearDirty(id);
24
- // Rebuild the parent wall so its cutout reflects the updated door geometry
25
- if (node.parentId) {
26
- useScene.getState().dirtyNodes.add(node.parentId);
27
- }
28
- });
29
- }, 3);
30
- return null;
31
- };
32
- function addBox(parent, material, w, h, d, x, y, z) {
33
- const m = new THREE.Mesh(new THREE.BoxGeometry(w, h, d), material);
34
- m.position.set(x, y, z);
35
- parent.add(m);
36
- }
37
- function updateDoorMesh(node, mesh) {
38
- // Root mesh is an invisible hitbox; all visuals live in child meshes
39
- mesh.geometry.dispose();
40
- mesh.geometry = new THREE.BoxGeometry(node.width, node.height, node.frameDepth);
41
- mesh.material = hitboxMaterial;
42
- // Sync transform from node (React may lag behind the system by a frame during drag)
43
- mesh.position.set(node.position[0], node.position[1], node.position[2]);
44
- mesh.rotation.set(node.rotation[0], node.rotation[1], node.rotation[2]);
45
- // Dispose and remove all old visual children; preserve 'cutout'
46
- for (const child of [...mesh.children]) {
47
- if (child.name === 'cutout')
48
- continue;
49
- if (child instanceof THREE.Mesh)
50
- child.geometry.dispose();
51
- mesh.remove(child);
52
- }
53
- const { width, height, frameThickness, frameDepth, threshold, thresholdHeight, segments, handle, handleHeight, handleSide, doorCloser, panicBar, panicBarHeight, contentPadding, hingesSide, } = node;
54
- // Leaf occupies the full opening (no bottom frame bar — door opens to floor)
55
- const leafW = width - 2 * frameThickness;
56
- const leafH = height - frameThickness; // only top frame
57
- const leafDepth = 0.04;
58
- // Leaf center is shifted down from door center by half the top frame
59
- const leafCenterY = -frameThickness / 2;
60
- // ── Frame members ──
61
- // Left post — full height
62
- addBox(mesh, baseMaterial, frameThickness, height, frameDepth, -width / 2 + frameThickness / 2, 0, 0);
63
- // Right post — full height
64
- addBox(mesh, baseMaterial, frameThickness, height, frameDepth, width / 2 - frameThickness / 2, 0, 0);
65
- // Head (top bar) — full width
66
- addBox(mesh, baseMaterial, width, frameThickness, frameDepth, 0, height / 2 - frameThickness / 2, 0);
67
- // ── Threshold (inside the frame) ──
68
- if (threshold) {
69
- addBox(mesh, baseMaterial, leafW, thresholdHeight, frameDepth, 0, -height / 2 + thresholdHeight / 2, 0);
70
- }
71
- // ── Leaf — contentPadding border strips (no full backing; glass areas are open) ──
72
- const cpX = contentPadding[0];
73
- const cpY = contentPadding[1];
74
- if (cpY > 0) {
75
- // Top strip
76
- addBox(mesh, baseMaterial, leafW, cpY, leafDepth, 0, leafCenterY + leafH / 2 - cpY / 2, 0);
77
- // Bottom strip
78
- addBox(mesh, baseMaterial, leafW, cpY, leafDepth, 0, leafCenterY - leafH / 2 + cpY / 2, 0);
79
- }
80
- if (cpX > 0) {
81
- const innerH = leafH - 2 * cpY;
82
- // Left strip
83
- addBox(mesh, baseMaterial, cpX, innerH, leafDepth, -leafW / 2 + cpX / 2, leafCenterY, 0);
84
- // Right strip
85
- addBox(mesh, baseMaterial, cpX, innerH, leafDepth, leafW / 2 - cpX / 2, leafCenterY, 0);
86
- }
87
- // Content area inside padding
88
- const contentW = leafW - 2 * cpX;
89
- const contentH = leafH - 2 * cpY;
90
- // ── Segments (stacked top to bottom within content area) ──
91
- const totalRatio = segments.reduce((sum, s) => sum + s.heightRatio, 0);
92
- const contentTop = leafCenterY + contentH / 2;
93
- let segY = contentTop;
94
- for (const seg of segments) {
95
- const segH = (seg.heightRatio / totalRatio) * contentH;
96
- const segCenterY = segY - segH / 2;
97
- const numCols = seg.columnRatios.length;
98
- const colSum = seg.columnRatios.reduce((a, b) => a + b, 0);
99
- const usableW = contentW - (numCols - 1) * seg.dividerThickness;
100
- const colWidths = seg.columnRatios.map((r) => (r / colSum) * usableW);
101
- // Column x-centers (relative to mesh center)
102
- const colXCenters = [];
103
- let cx = -contentW / 2;
104
- for (let c = 0; c < numCols; c++) {
105
- colXCenters.push(cx + colWidths[c] / 2);
106
- cx += colWidths[c];
107
- if (c < numCols - 1)
108
- cx += seg.dividerThickness;
109
- }
110
- // Column dividers within this segment
111
- cx = -contentW / 2;
112
- for (let c = 0; c < numCols - 1; c++) {
113
- cx += colWidths[c];
114
- addBox(mesh, baseMaterial, seg.dividerThickness, segH, leafDepth + 0.001, cx + seg.dividerThickness / 2, segCenterY, 0);
115
- cx += seg.dividerThickness;
116
- }
117
- // Segment content per column
118
- for (let c = 0; c < numCols; c++) {
119
- const colW = colWidths[c];
120
- const colX = colXCenters[c];
121
- if (seg.type === 'glass') {
122
- // Glass only — no opaque backing so it's truly transparent
123
- const glassDepth = Math.max(0.004, leafDepth * 0.15);
124
- addBox(mesh, glassMaterial, colW, segH, glassDepth, colX, segCenterY, 0);
125
- }
126
- else if (seg.type === 'panel') {
127
- // Opaque leaf backing for this column
128
- addBox(mesh, baseMaterial, colW, segH, leafDepth, colX, segCenterY, 0);
129
- // Raised panel detail
130
- const panelW = colW - 2 * seg.panelInset;
131
- const panelH = segH - 2 * seg.panelInset;
132
- if (panelW > 0.01 && panelH > 0.01) {
133
- const effectiveDepth = Math.abs(seg.panelDepth) < 0.002 ? 0.005 : Math.abs(seg.panelDepth);
134
- const panelZ = leafDepth / 2 + effectiveDepth / 2;
135
- addBox(mesh, baseMaterial, panelW, panelH, effectiveDepth, colX, segCenterY, panelZ);
136
- }
137
- }
138
- else {
139
- // 'empty' — opaque backing, no detail
140
- addBox(mesh, baseMaterial, colW, segH, leafDepth, colX, segCenterY, 0);
141
- }
142
- }
143
- segY -= segH;
144
- }
145
- // ── Handle ──
146
- if (handle) {
147
- // Convert from floor-based height to mesh-center-based Y
148
- const handleY = handleHeight - height / 2;
149
- // Handle grip sits on the front face (+Z) of the leaf
150
- const faceZ = leafDepth / 2;
151
- // X position: handleSide refers to which side the grip is on
152
- const handleX = handleSide === 'right' ? leafW / 2 - 0.045 : -leafW / 2 + 0.045;
153
- // Backplate
154
- addBox(mesh, baseMaterial, 0.028, 0.14, 0.01, handleX, handleY, faceZ + 0.005);
155
- // Grip lever
156
- addBox(mesh, baseMaterial, 0.022, 0.1, 0.035, handleX, handleY, faceZ + 0.025);
157
- }
158
- // ── Door closer (commercial hardware at top) ──
159
- if (doorCloser) {
160
- const closerY = leafCenterY + leafH / 2 - 0.04;
161
- // Body
162
- addBox(mesh, baseMaterial, 0.28, 0.055, 0.055, 0, closerY, leafDepth / 2 + 0.03);
163
- // Arm (simplified as thin bar to frame side)
164
- addBox(mesh, baseMaterial, 0.14, 0.015, 0.015, leafW / 4, closerY + 0.025, leafDepth / 2 + 0.015);
165
- }
166
- // ── Panic bar ──
167
- if (panicBar) {
168
- const barY = panicBarHeight - height / 2;
169
- addBox(mesh, baseMaterial, leafW * 0.72, 0.04, 0.055, 0, barY, leafDepth / 2 + 0.03);
170
- }
171
- // ── Hinges (3 knuckle-style hinges on the hinge side) ──
172
- {
173
- const hingeX = hingesSide === 'right' ? leafW / 2 - 0.012 : -leafW / 2 + 0.012;
174
- const hingeZ = 0; // centered in leaf depth
175
- const hingeH = 0.1;
176
- const hingeW = 0.024;
177
- const hingeD = leafDepth + 0.016;
178
- // Bottom hinge ~0.25m from floor, middle hinge, top hinge ~0.25m from top
179
- const leafBottom = leafCenterY - leafH / 2;
180
- const leafTop = leafCenterY + leafH / 2;
181
- addBox(mesh, baseMaterial, hingeW, hingeH, hingeD, hingeX, leafBottom + 0.25, hingeZ);
182
- addBox(mesh, baseMaterial, hingeW, hingeH, hingeD, hingeX, (leafBottom + leafTop) / 2, hingeZ);
183
- addBox(mesh, baseMaterial, hingeW, hingeH, hingeD, hingeX, leafTop - 0.25, hingeZ);
184
- }
185
- // ── Cutout (for wall CSG) — always full door dimensions, 1m deep ──
186
- let cutout = mesh.getObjectByName('cutout');
187
- if (!cutout) {
188
- cutout = new THREE.Mesh();
189
- cutout.name = 'cutout';
190
- mesh.add(cutout);
191
- }
192
- cutout.geometry.dispose();
193
- cutout.geometry = new THREE.BoxGeometry(node.width, node.height, 1.0);
194
- cutout.visible = false;
195
- }
@@ -1,2 +0,0 @@
1
- export declare const FenceSystem: () => null;
2
- //# sourceMappingURL=fence-system.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"fence-system.d.ts","sourceRoot":"","sources":["../../../src/systems/fence/fence-system.tsx"],"names":[],"mappings":"AAyQA,eAAO,MAAM,WAAW,YAiBvB,CAAA"}
@@ -1,187 +0,0 @@
1
- import { useFrame } from '@react-three/fiber';
2
- import * as THREE from 'three';
3
- import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
4
- import { sceneRegistry } from '../../hooks/scene-registry/scene-registry';
5
- import useScene from '../../store/use-scene';
6
- import { getWallCurveFrameAt, getWallCurveLength } from '../wall/wall-curve';
7
- const MIN_CURVE_SEGMENT_LENGTH = 0.18;
8
- function createFencePartGeometry(part) {
9
- const geometry = new THREE.BoxGeometry(1, 1, 1);
10
- geometry.scale(part.scale[0], part.scale[1], part.scale[2]);
11
- if (part.rotationY) {
12
- geometry.rotateY(part.rotationY);
13
- }
14
- geometry.translate(part.position[0], part.position[1], part.position[2]);
15
- applyFenceUVs(geometry);
16
- return geometry;
17
- }
18
- function getFencePointAt(fence, t) {
19
- const frame = getWallCurveFrameAt(fence, t);
20
- return {
21
- point: frame.point,
22
- tangentAngle: Math.atan2(frame.tangent.y, frame.tangent.x),
23
- };
24
- }
25
- function createStraightFenceSpanPart(start, end, centerY, height, depth) {
26
- const dx = end[0] - start[0];
27
- const dz = end[1] - start[1];
28
- const length = Math.hypot(dx, dz);
29
- if (length <= 1e-4) {
30
- return null;
31
- }
32
- return {
33
- position: [(start[0] + end[0]) / 2, centerY, (start[1] + end[1]) / 2],
34
- rotationY: -Math.atan2(dz, dx),
35
- scale: [length, height, depth],
36
- };
37
- }
38
- function createFenceCurveSpanParts(fence, startT, endT, centerY, height, depth) {
39
- const parts = [];
40
- const frameCount = Math.max(1, Math.ceil((getWallCurveLength(fence) * Math.max(1e-4, endT - startT)) / MIN_CURVE_SEGMENT_LENGTH));
41
- let previous = getFencePointAt(fence, startT);
42
- for (let index = 1; index <= frameCount; index += 1) {
43
- const t = startT + (endT - startT) * (index / frameCount);
44
- const current = getFencePointAt(fence, t);
45
- const segment = createStraightFenceSpanPart([previous.point.x, previous.point.y], [current.point.x, current.point.y], centerY, height, depth);
46
- if (segment) {
47
- parts.push(segment);
48
- }
49
- previous = current;
50
- }
51
- return parts;
52
- }
53
- function applyFenceUVs(geometry) {
54
- const position = geometry.getAttribute('position');
55
- const normal = geometry.getAttribute('normal');
56
- if (!(position && normal))
57
- return;
58
- const uvs = new Float32Array(position.count * 2);
59
- let minX = Number.POSITIVE_INFINITY;
60
- let minY = Number.POSITIVE_INFINITY;
61
- let minZ = Number.POSITIVE_INFINITY;
62
- for (let index = 0; index < position.count; index += 1) {
63
- minX = Math.min(minX, position.getX(index));
64
- minY = Math.min(minY, position.getY(index));
65
- minZ = Math.min(minZ, position.getZ(index));
66
- }
67
- for (let index = 0; index < position.count; index += 1) {
68
- const px = position.getX(index);
69
- const py = position.getY(index);
70
- const pz = position.getZ(index);
71
- const nx = Math.abs(normal.getX(index));
72
- const ny = Math.abs(normal.getY(index));
73
- const nz = Math.abs(normal.getZ(index));
74
- let u = 0;
75
- let v = 0;
76
- if (ny >= nx && ny >= nz) {
77
- u = px - minX;
78
- v = pz - minZ;
79
- }
80
- else if (nx >= nz) {
81
- u = pz - minZ;
82
- v = py - minY;
83
- }
84
- else {
85
- u = px - minX;
86
- v = py - minY;
87
- }
88
- uvs[index * 2] = u;
89
- uvs[index * 2 + 1] = v;
90
- }
91
- geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2));
92
- geometry.setAttribute('uv2', new THREE.Float32BufferAttribute(uvs.slice(), 2));
93
- }
94
- function getStyleDefaults(style) {
95
- if (style === 'privacy') {
96
- return { spacingFactor: 0.42, postFactor: 1.35, baseFactor: 1.2, topFactor: 1.2 };
97
- }
98
- if (style === 'rail') {
99
- return { spacingFactor: 0.68, postFactor: 0.8, baseFactor: 0.85, topFactor: 0.85 };
100
- }
101
- return { spacingFactor: 0.3, postFactor: 0.55, baseFactor: 1, topFactor: 0.75 };
102
- }
103
- function createFenceParts(fence) {
104
- const parts = [];
105
- const length = Math.max(getWallCurveLength(fence), 0.01);
106
- const panelDepth = Math.max(fence.thickness, 0.03);
107
- const clearance = Math.max(fence.groundClearance, 0);
108
- const styleDefaults = getStyleDefaults(fence.style);
109
- const baseHeight = Math.max(fence.baseHeight * styleDefaults.baseFactor, 0.04);
110
- const topRailHeight = Math.max(fence.topRailHeight * styleDefaults.topFactor, 0.01);
111
- const verticalHeight = Math.max(fence.height - baseHeight - topRailHeight, 0.08);
112
- const postWidth = Math.max(fence.postSize * styleDefaults.postFactor, 0.01);
113
- const spacing = Math.max(fence.postSpacing * styleDefaults.spacingFactor, postWidth * 1.2);
114
- const edgeInset = Math.max(fence.edgeInset ?? 0.015, 0.005);
115
- const isFloating = fence.baseStyle === 'floating';
116
- const baseY = isFloating ? clearance : 0;
117
- const effectiveBaseHeight = baseHeight;
118
- const startInsetT = Math.min(0.499, edgeInset / length);
119
- const endInsetT = Math.max(0.501, 1 - edgeInset / length);
120
- if (!isFloating) {
121
- parts.push(...createFenceCurveSpanParts(fence, 0, 1, baseY + effectiveBaseHeight / 2, effectiveBaseHeight, panelDepth * 1.05));
122
- parts.push(...createFenceCurveSpanParts(fence, 0, 1, baseY + effectiveBaseHeight + verticalHeight * 0.15, topRailHeight * 0.8, panelDepth * 0.35));
123
- }
124
- const count = Math.max(2, Math.floor((length - edgeInset * 2) / spacing) + 1);
125
- const verticalY = baseY + effectiveBaseHeight + verticalHeight / 2;
126
- for (let index = 0; index < count; index += 1) {
127
- const t = count === 1 ? 0.5 : startInsetT + (endInsetT - startInsetT) * (index / (count - 1));
128
- const frame = getFencePointAt(fence, t);
129
- const isEdgePost = index === 0 || index === count - 1;
130
- const postHeight = isFloating && isEdgePost
131
- ? effectiveBaseHeight + verticalHeight + topRailHeight + clearance
132
- : verticalHeight;
133
- const postY = isFloating && isEdgePost ? postHeight / 2 : verticalY;
134
- parts.push({
135
- position: [frame.point.x, postY, frame.point.y],
136
- rotationY: -frame.tangentAngle,
137
- scale: [postWidth, postHeight, Math.max(panelDepth * 0.35, 0.012)],
138
- });
139
- }
140
- parts.push(...createFenceCurveSpanParts(fence, 0, 1, baseY + effectiveBaseHeight + verticalHeight + topRailHeight / 2, topRailHeight, Math.max(panelDepth * 0.55, 0.018)));
141
- if (isFloating) {
142
- parts.push(...createFenceCurveSpanParts(fence, 0, 1, baseY + effectiveBaseHeight + topRailHeight / 2, topRailHeight, Math.max(panelDepth * 0.55, 0.018)));
143
- }
144
- return parts;
145
- }
146
- function generateFenceGeometry(fence) {
147
- const parts = createFenceParts(fence);
148
- const geometries = parts.map(createFencePartGeometry);
149
- const merged = mergeGeometries(geometries, false) ?? new THREE.BufferGeometry();
150
- geometries.forEach((geometry) => geometry.dispose());
151
- const mergedUv = merged.getAttribute('uv');
152
- if (mergedUv) {
153
- merged.setAttribute('uv2', new THREE.Float32BufferAttribute(Array.from(mergedUv.array), 2));
154
- }
155
- merged.computeVertexNormals();
156
- return merged;
157
- }
158
- function updateFenceGeometry(fenceId) {
159
- const node = useScene.getState().nodes[fenceId];
160
- if (!node || node.type !== 'fence')
161
- return;
162
- const mesh = sceneRegistry.nodes.get(fenceId);
163
- if (!mesh)
164
- return;
165
- const newGeometry = generateFenceGeometry(node);
166
- mesh.geometry.dispose();
167
- mesh.geometry = newGeometry;
168
- mesh.position.set(0, 0, 0);
169
- mesh.rotation.set(0, 0, 0);
170
- }
171
- export const FenceSystem = () => {
172
- const dirtyNodes = useScene((state) => state.dirtyNodes);
173
- const clearDirty = useScene((state) => state.clearDirty);
174
- useFrame(() => {
175
- if (dirtyNodes.size === 0)
176
- return;
177
- const nodes = useScene.getState().nodes;
178
- dirtyNodes.forEach((id) => {
179
- const node = nodes[id];
180
- if (!node || node.type !== 'fence')
181
- return;
182
- updateFenceGeometry(id);
183
- clearDirty(id);
184
- });
185
- }, 4);
186
- return null;
187
- };
@@ -1,2 +0,0 @@
1
- export declare const ItemSystem: () => null;
2
- //# sourceMappingURL=item-system.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"item-system.d.ts","sourceRoot":"","sources":["../../../src/systems/item/item-system.tsx"],"names":[],"mappings":"AAYA,eAAO,MAAM,UAAU,YA6CtB,CAAA"}
@@ -1,48 +0,0 @@
1
- import { useFrame } from '@react-three/fiber';
2
- import { sceneRegistry } from '../../hooks/scene-registry/scene-registry';
3
- import { spatialGridManager } from '../../hooks/spatial-grid/spatial-grid-manager';
4
- import { resolveLevelId } from '../../hooks/spatial-grid/spatial-grid-sync';
5
- import { getScaledDimensions } from '../../schema';
6
- import useScene from '../../store/use-scene';
7
- // ============================================================================
8
- // ITEM SYSTEM
9
- // ============================================================================
10
- export const ItemSystem = () => {
11
- const dirtyNodes = useScene((state) => state.dirtyNodes);
12
- const clearDirty = useScene((state) => state.clearDirty);
13
- useFrame(() => {
14
- if (dirtyNodes.size === 0)
15
- return;
16
- const nodes = useScene.getState().nodes;
17
- dirtyNodes.forEach((id) => {
18
- const node = nodes[id];
19
- if (!node || node.type !== 'item')
20
- return;
21
- const item = node;
22
- const mesh = sceneRegistry.nodes.get(id);
23
- if (!mesh)
24
- return;
25
- if (item.asset.attachTo === 'wall-side') {
26
- // Wall-attached item: offset Z by half the parent wall's thickness
27
- const parentWall = item.parentId ? nodes[item.parentId] : undefined;
28
- if (parentWall && parentWall.type === 'wall') {
29
- const wallThickness = parentWall.thickness ?? 0.1;
30
- const side = item.side === 'front' ? 1 : -1;
31
- mesh.position.z = (wallThickness / 2) * side;
32
- }
33
- }
34
- else if (!item.asset.attachTo) {
35
- // If parented to another item (surface placement), R3F handles positioning via the hierarchy
36
- const parentNode = item.parentId ? nodes[item.parentId] : undefined;
37
- if (parentNode?.type !== 'item') {
38
- // Floor item: elevate by slab height (using full footprint overlap)
39
- const levelId = resolveLevelId(item, nodes);
40
- const slabElevation = spatialGridManager.getSlabElevationForItem(levelId, item.position, getScaledDimensions(item), item.rotation);
41
- mesh.position.y = slabElevation + item.position[1];
42
- }
43
- }
44
- clearDirty(id);
45
- });
46
- }, 2);
47
- return null;
48
- };
@@ -1,16 +0,0 @@
1
- import * as THREE from 'three';
2
- import { Brush } from 'three-bvh-csg';
3
- import type { RoofSegmentNode } from '../../schema';
4
- export declare const RoofSystem: () => null;
5
- /**
6
- * Generate complete hollow-shell geometry for a roof segment.
7
- * Ports the prototype's CSG approach using three-bvh-csg.
8
- */
9
- export declare function getRoofSegmentBrushes(node: RoofSegmentNode): {
10
- deckSlab: Brush;
11
- shinSlab: Brush;
12
- wallBrush: Brush;
13
- innerBrush: Brush;
14
- } | null;
15
- export declare function generateRoofSegmentGeometry(node: RoofSegmentNode): THREE.BufferGeometry;
16
- //# sourceMappingURL=roof-system.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"roof-system.d.ts","sourceRoot":"","sources":["../../../src/systems/roof/roof-system.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAY,KAAK,EAA0B,MAAM,eAAe,CAAA;AAGvE,OAAO,KAAK,EAAgC,eAAe,EAAE,MAAM,cAAc,CAAA;AAgCjF,eAAO,MAAM,UAAU,YAuFtB,CAAA;AAwLD;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,eAAe,GACpB;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,KAAK,CAAC;IAAC,SAAS,EAAE,KAAK,CAAC;IAAC,UAAU,EAAE,KAAK,CAAA;CAAE,GAAG,IAAI,CAsRlF;AAED,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,eAAe,GAAG,KAAK,CAAC,cAAc,CAqDvF"}