@pascal-app/core 0.5.0 → 0.6.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 +39 -4
- package/dist/events/bus.d.ts.map +1 -1
- package/dist/events/bus.js +1 -1
- package/dist/hooks/scene-registry/scene-registry.d.ts +1 -0
- package/dist/hooks/scene-registry/scene-registry.d.ts.map +1 -1
- package/dist/hooks/scene-registry/scene-registry.js +1 -0
- package/dist/hooks/spatial-grid/spatial-grid.d.ts +2 -0
- package/dist/hooks/spatial-grid/spatial-grid.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid.js +43 -20
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/lib/polygon-geometry.d.ts +3 -0
- package/dist/lib/polygon-geometry.d.ts.map +1 -0
- package/dist/lib/polygon-geometry.js +90 -0
- package/dist/lib/space-detection.d.ts +10 -17
- package/dist/lib/space-detection.d.ts.map +1 -1
- package/dist/lib/space-detection.js +666 -453
- package/dist/material-library.d.ts +18 -0
- package/dist/material-library.d.ts.map +1 -0
- package/dist/material-library.js +603 -0
- package/dist/schema/index.d.ts +10 -4
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +6 -4
- package/dist/schema/material.d.ts +109 -0
- package/dist/schema/material.d.ts.map +1 -1
- package/dist/schema/material.js +52 -0
- package/dist/schema/nodes/ceiling.d.ts +10 -0
- package/dist/schema/nodes/ceiling.d.ts.map +1 -1
- package/dist/schema/nodes/ceiling.js +6 -0
- package/dist/schema/nodes/door.d.ts +1 -0
- package/dist/schema/nodes/door.d.ts.map +1 -1
- package/dist/schema/nodes/fence.d.ts +85 -0
- package/dist/schema/nodes/fence.d.ts.map +1 -0
- package/dist/schema/nodes/fence.js +34 -0
- package/dist/schema/nodes/item.d.ts +2 -2
- package/dist/schema/nodes/level.d.ts +1 -1
- package/dist/schema/nodes/level.d.ts.map +1 -1
- package/dist/schema/nodes/level.js +2 -0
- package/dist/schema/nodes/roof-segment.d.ts +2 -0
- package/dist/schema/nodes/roof-segment.d.ts.map +1 -1
- package/dist/schema/nodes/roof-segment.js +1 -0
- package/dist/schema/nodes/roof.d.ts +108 -0
- package/dist/schema/nodes/roof.d.ts.map +1 -1
- package/dist/schema/nodes/roof.js +58 -2
- package/dist/schema/nodes/site.d.ts +1 -1
- package/dist/schema/nodes/slab.d.ts +10 -0
- package/dist/schema/nodes/slab.d.ts.map +1 -1
- package/dist/schema/nodes/slab.js +7 -0
- package/dist/schema/nodes/stair-segment.d.ts +2 -0
- package/dist/schema/nodes/stair-segment.d.ts.map +1 -1
- package/dist/schema/nodes/stair-segment.js +1 -0
- package/dist/schema/nodes/stair.d.ts +164 -0
- package/dist/schema/nodes/stair.d.ts.map +1 -1
- package/dist/schema/nodes/stair.js +106 -5
- package/dist/schema/nodes/surface-hole-metadata.d.ts +10 -0
- package/dist/schema/nodes/surface-hole-metadata.d.ts.map +1 -0
- package/dist/schema/nodes/surface-hole-metadata.js +5 -0
- package/dist/schema/nodes/wall.d.ts +87 -1
- package/dist/schema/nodes/wall.d.ts.map +1 -1
- package/dist/schema/nodes/wall.js +45 -4
- package/dist/schema/nodes/window.d.ts +1 -0
- package/dist/schema/nodes/window.d.ts.map +1 -1
- package/dist/schema/types.d.ts +406 -4
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/types.js +2 -0
- package/dist/store/actions/node-actions.d.ts +1 -1
- package/dist/store/actions/node-actions.d.ts.map +1 -1
- package/dist/store/actions/node-actions.js +175 -0
- package/dist/store/history-control.d.ts +14 -0
- package/dist/store/history-control.d.ts.map +1 -0
- package/dist/store/history-control.js +22 -0
- package/dist/store/use-scene.d.ts.map +1 -1
- package/dist/store/use-scene.js +249 -3
- package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
- package/dist/systems/ceiling/ceiling-system.js +7 -0
- package/dist/systems/fence/fence-system.d.ts +2 -0
- package/dist/systems/fence/fence-system.d.ts.map +1 -0
- package/dist/systems/fence/fence-system.js +187 -0
- package/dist/systems/roof/roof-system.d.ts.map +1 -1
- package/dist/systems/roof/roof-system.js +31 -1
- package/dist/systems/slab/slab-system.d.ts.map +1 -1
- package/dist/systems/slab/slab-system.js +45 -8
- package/dist/systems/stair/stair-opening-sync.d.ts +6 -0
- package/dist/systems/stair/stair-opening-sync.d.ts.map +1 -0
- package/dist/systems/stair/stair-opening-sync.js +515 -0
- package/dist/systems/stair/stair-system.d.ts.map +1 -1
- package/dist/systems/stair/stair-system.js +432 -10
- package/dist/systems/wall/wall-curve.d.ts +43 -0
- package/dist/systems/wall/wall-curve.d.ts.map +1 -0
- package/dist/systems/wall/wall-curve.js +176 -0
- package/dist/systems/wall/wall-footprint.d.ts.map +1 -1
- package/dist/systems/wall/wall-footprint.js +16 -2
- package/dist/systems/wall/wall-mitering.d.ts +7 -0
- package/dist/systems/wall/wall-mitering.d.ts.map +1 -1
- package/dist/systems/wall/wall-mitering.js +76 -3
- package/dist/systems/wall/wall-system.d.ts.map +1 -1
- package/dist/systems/wall/wall-system.js +202 -2
- package/package.json +3 -3
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
const CURVE_EPSILON = 1e-6;
|
|
2
|
+
const DEFAULT_SAMPLE_SEGMENTS = 24;
|
|
3
|
+
function clamp01(value) {
|
|
4
|
+
return Math.max(0, Math.min(1, value));
|
|
5
|
+
}
|
|
6
|
+
function lerp(a, b, t) {
|
|
7
|
+
return a + (b - a) * t;
|
|
8
|
+
}
|
|
9
|
+
function distance(a, b) {
|
|
10
|
+
return Math.hypot(b.x - a.x, b.y - a.y);
|
|
11
|
+
}
|
|
12
|
+
export function getWallStartPoint(wall) {
|
|
13
|
+
return { x: wall.start[0], y: wall.start[1] };
|
|
14
|
+
}
|
|
15
|
+
export function getWallEndPoint(wall) {
|
|
16
|
+
return { x: wall.end[0], y: wall.end[1] };
|
|
17
|
+
}
|
|
18
|
+
export function getWallChordLength(wall) {
|
|
19
|
+
return distance(getWallStartPoint(wall), getWallEndPoint(wall));
|
|
20
|
+
}
|
|
21
|
+
export function getMaxWallCurveOffset(wall) {
|
|
22
|
+
return getWallChordLength(wall) / 2;
|
|
23
|
+
}
|
|
24
|
+
export function getWallStraightSnapOffset(wall) {
|
|
25
|
+
return Math.min(0.03, Math.max(0.005, getWallChordLength(wall) * 0.005));
|
|
26
|
+
}
|
|
27
|
+
function clampCurveOffset(wall, offset) {
|
|
28
|
+
const maxOffset = getMaxWallCurveOffset(wall);
|
|
29
|
+
if (!Number.isFinite(maxOffset) || maxOffset < CURVE_EPSILON) {
|
|
30
|
+
return 0;
|
|
31
|
+
}
|
|
32
|
+
return Math.max(-maxOffset, Math.min(maxOffset, offset));
|
|
33
|
+
}
|
|
34
|
+
export function normalizeWallCurveOffset(wall, offset) {
|
|
35
|
+
const clamped = clampCurveOffset(wall, offset);
|
|
36
|
+
return Math.abs(clamped) <= getWallStraightSnapOffset(wall) ? 0 : clamped;
|
|
37
|
+
}
|
|
38
|
+
export function getClampedWallCurveOffset(wall) {
|
|
39
|
+
const value = wall.curveOffset ?? 0;
|
|
40
|
+
const normalized = normalizeWallCurveOffset(wall, value);
|
|
41
|
+
return Math.abs(normalized) > CURVE_EPSILON ? normalized : 0;
|
|
42
|
+
}
|
|
43
|
+
export function isCurvedWall(wall) {
|
|
44
|
+
return Math.abs(getClampedWallCurveOffset(wall)) > CURVE_EPSILON;
|
|
45
|
+
}
|
|
46
|
+
export function getWallChordFrame(wall) {
|
|
47
|
+
const start = getWallStartPoint(wall);
|
|
48
|
+
const end = getWallEndPoint(wall);
|
|
49
|
+
const dx = end.x - start.x;
|
|
50
|
+
const dy = end.y - start.y;
|
|
51
|
+
const length = Math.hypot(dx, dy);
|
|
52
|
+
if (length < CURVE_EPSILON) {
|
|
53
|
+
return {
|
|
54
|
+
start,
|
|
55
|
+
end,
|
|
56
|
+
midpoint: start,
|
|
57
|
+
tangent: { x: 1, y: 0 },
|
|
58
|
+
normal: { x: 0, y: 1 },
|
|
59
|
+
length: 0,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
start,
|
|
64
|
+
end,
|
|
65
|
+
midpoint: {
|
|
66
|
+
x: (start.x + end.x) / 2,
|
|
67
|
+
y: (start.y + end.y) / 2,
|
|
68
|
+
},
|
|
69
|
+
tangent: { x: dx / length, y: dy / length },
|
|
70
|
+
normal: { x: -dy / length, y: dx / length },
|
|
71
|
+
length,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function getWallArcData(wall) {
|
|
75
|
+
const chord = getWallChordFrame(wall);
|
|
76
|
+
const sagitta = getClampedWallCurveOffset(wall);
|
|
77
|
+
if (Math.abs(sagitta) <= CURVE_EPSILON || chord.length < CURVE_EPSILON) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
const absSagitta = Math.abs(sagitta);
|
|
81
|
+
const radius = chord.length * chord.length / (8 * absSagitta) + absSagitta / 2;
|
|
82
|
+
const centerOffset = radius - absSagitta;
|
|
83
|
+
const direction = Math.sign(sagitta) || 1;
|
|
84
|
+
const center = {
|
|
85
|
+
x: chord.midpoint.x + chord.normal.x * centerOffset * direction,
|
|
86
|
+
y: chord.midpoint.y + chord.normal.y * centerOffset * direction,
|
|
87
|
+
};
|
|
88
|
+
const startAngle = Math.atan2(chord.start.y - center.y, chord.start.x - center.x);
|
|
89
|
+
const endAngle = Math.atan2(chord.end.y - center.y, chord.end.x - center.x);
|
|
90
|
+
let delta = endAngle - startAngle;
|
|
91
|
+
if (direction > 0) {
|
|
92
|
+
while (delta <= 0)
|
|
93
|
+
delta += Math.PI * 2;
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
while (delta >= 0)
|
|
97
|
+
delta -= Math.PI * 2;
|
|
98
|
+
}
|
|
99
|
+
return { center, radius, startAngle, delta, direction };
|
|
100
|
+
}
|
|
101
|
+
export function getWallCurveFrameAt(wall, t) {
|
|
102
|
+
const chord = getWallChordFrame(wall);
|
|
103
|
+
if (!isCurvedWall(wall) || chord.length < CURVE_EPSILON) {
|
|
104
|
+
return {
|
|
105
|
+
point: {
|
|
106
|
+
x: lerp(chord.start.x, chord.end.x, clamp01(t)),
|
|
107
|
+
y: lerp(chord.start.y, chord.end.y, clamp01(t)),
|
|
108
|
+
},
|
|
109
|
+
tangent: chord.tangent,
|
|
110
|
+
normal: chord.normal,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
const arc = getWallArcData(wall);
|
|
114
|
+
if (!arc) {
|
|
115
|
+
return {
|
|
116
|
+
point: chord.midpoint,
|
|
117
|
+
tangent: chord.tangent,
|
|
118
|
+
normal: chord.normal,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
const angle = arc.startAngle + arc.delta * clamp01(t);
|
|
122
|
+
const point = {
|
|
123
|
+
x: arc.center.x + Math.cos(angle) * arc.radius,
|
|
124
|
+
y: arc.center.y + Math.sin(angle) * arc.radius,
|
|
125
|
+
};
|
|
126
|
+
const tangent = arc.direction > 0
|
|
127
|
+
? { x: -Math.sin(angle), y: Math.cos(angle) }
|
|
128
|
+
: { x: Math.sin(angle), y: -Math.cos(angle) };
|
|
129
|
+
return {
|
|
130
|
+
point,
|
|
131
|
+
tangent,
|
|
132
|
+
normal: {
|
|
133
|
+
x: -tangent.y,
|
|
134
|
+
y: tangent.x,
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
export function getWallMidpointHandlePoint(wall) {
|
|
139
|
+
return getWallCurveFrameAt(wall, 0.5).point;
|
|
140
|
+
}
|
|
141
|
+
export function sampleWallCenterline(wall, segments = DEFAULT_SAMPLE_SEGMENTS) {
|
|
142
|
+
const count = Math.max(1, segments);
|
|
143
|
+
return Array.from({ length: count + 1 }, (_, index) => getWallCurveFrameAt(wall, index / count).point);
|
|
144
|
+
}
|
|
145
|
+
export function getWallCurveLength(wall, segments = DEFAULT_SAMPLE_SEGMENTS) {
|
|
146
|
+
const points = sampleWallCenterline(wall, segments);
|
|
147
|
+
let totalLength = 0;
|
|
148
|
+
for (let index = 1; index < points.length; index += 1) {
|
|
149
|
+
totalLength += distance(points[index - 1], points[index]);
|
|
150
|
+
}
|
|
151
|
+
return totalLength;
|
|
152
|
+
}
|
|
153
|
+
export function getWallSurfacePolygon(wall, segments = DEFAULT_SAMPLE_SEGMENTS, miterOverrides) {
|
|
154
|
+
const halfThickness = (wall.thickness ?? 0.1) / 2;
|
|
155
|
+
const count = Math.max(1, segments);
|
|
156
|
+
const left = [];
|
|
157
|
+
const right = [];
|
|
158
|
+
for (let index = 0; index <= count; index += 1) {
|
|
159
|
+
const frame = getWallCurveFrameAt(wall, index / count);
|
|
160
|
+
left.push({
|
|
161
|
+
x: frame.point.x + frame.normal.x * halfThickness,
|
|
162
|
+
y: frame.point.y + frame.normal.y * halfThickness,
|
|
163
|
+
});
|
|
164
|
+
right.push({
|
|
165
|
+
x: frame.point.x - frame.normal.x * halfThickness,
|
|
166
|
+
y: frame.point.y - frame.normal.y * halfThickness,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
if (left.length > 0 && right.length > 0) {
|
|
170
|
+
left[0] = miterOverrides?.startLeft ?? left[0];
|
|
171
|
+
right[0] = miterOverrides?.startRight ?? right[0];
|
|
172
|
+
left[left.length - 1] = miterOverrides?.endLeft ?? left[left.length - 1];
|
|
173
|
+
right[right.length - 1] = miterOverrides?.endRight ?? right[right.length - 1];
|
|
174
|
+
}
|
|
175
|
+
return [...right, ...left.reverse()];
|
|
176
|
+
}
|
|
@@ -1 +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;
|
|
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;AAE5C,OAAO,EAEL,KAAK,OAAO,EAEZ,KAAK,aAAa,EACnB,MAAM,iBAAiB,CAAA;AAExB,eAAO,MAAM,sBAAsB,MAAM,CAAA;AACzC,eAAO,MAAM,mBAAmB,MAAM,CAAA;AAGtC,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAE3D;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,GAAG,OAAO,EAAE,CA6D5F"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getWallSurfacePolygon, isCurvedWall } from './wall-curve';
|
|
2
|
+
import { getWallMiterBoundaryPoints, pointToKey, } from './wall-mitering';
|
|
2
3
|
export const DEFAULT_WALL_THICKNESS = 0.1;
|
|
3
4
|
export const DEFAULT_WALL_HEIGHT = 2.5;
|
|
5
|
+
const CURVED_WALL_SURFACE_SEGMENTS = 24;
|
|
4
6
|
export function getWallThickness(wallNode) {
|
|
5
7
|
return wallNode.thickness ?? DEFAULT_WALL_THICKNESS;
|
|
6
8
|
}
|
|
@@ -20,6 +22,19 @@ export function getWallPlanFootprint(wallNode, miterData) {
|
|
|
20
22
|
const keyEnd = pointToKey(wallEnd);
|
|
21
23
|
const startJunction = junctionData.get(keyStart)?.get(wallNode.id);
|
|
22
24
|
const endJunction = junctionData.get(keyEnd)?.get(wallNode.id);
|
|
25
|
+
if (isCurvedWall(wallNode)) {
|
|
26
|
+
const boundaryPoints = getWallMiterBoundaryPoints(wallNode, miterData);
|
|
27
|
+
if (!boundaryPoints) {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
const { startLeft, startRight, endLeft, endRight } = boundaryPoints;
|
|
31
|
+
return getWallSurfacePolygon(wallNode, CURVED_WALL_SURFACE_SEGMENTS, {
|
|
32
|
+
endLeft,
|
|
33
|
+
endRight,
|
|
34
|
+
startLeft,
|
|
35
|
+
startRight,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
23
38
|
const pStartLeft = startJunction?.left || {
|
|
24
39
|
x: wallStart.x + nUnit.x * halfT,
|
|
25
40
|
y: wallStart.y + nUnit.y * halfT,
|
|
@@ -28,7 +43,6 @@ export function getWallPlanFootprint(wallNode, miterData) {
|
|
|
28
43
|
x: wallStart.x - nUnit.x * halfT,
|
|
29
44
|
y: wallStart.y - nUnit.y * halfT,
|
|
30
45
|
};
|
|
31
|
-
// Junction offsets are stored relative to the outgoing direction.
|
|
32
46
|
const pEndLeft = endJunction?.right || {
|
|
33
47
|
x: wallEnd.x + nUnit.x * halfT,
|
|
34
48
|
y: wallEnd.y + nUnit.y * halfT,
|
|
@@ -3,6 +3,12 @@ export interface Point2D {
|
|
|
3
3
|
x: number;
|
|
4
4
|
y: number;
|
|
5
5
|
}
|
|
6
|
+
export interface WallMiterBoundaryPoints {
|
|
7
|
+
startLeft: Point2D;
|
|
8
|
+
startRight: Point2D;
|
|
9
|
+
endLeft: Point2D;
|
|
10
|
+
endRight: Point2D;
|
|
11
|
+
}
|
|
6
12
|
type WallIntersections = Map<string, {
|
|
7
13
|
left?: Point2D;
|
|
8
14
|
right?: Point2D;
|
|
@@ -24,6 +30,7 @@ export interface WallMiterData {
|
|
|
24
30
|
* Calculates miter data for all walls on a level
|
|
25
31
|
*/
|
|
26
32
|
export declare function calculateLevelMiters(walls: WallNode[]): WallMiterData;
|
|
33
|
+
export declare function getWallMiterBoundaryPoints(wall: WallNode, miterData: WallMiterData): WallMiterBoundaryPoints | null;
|
|
27
34
|
/**
|
|
28
35
|
* Gets wall IDs that share junctions with the given walls
|
|
29
36
|
*/
|
|
@@ -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;
|
|
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,3 +1,4 @@
|
|
|
1
|
+
import { getWallCurveFrameAt, isCurvedWall } from './wall-curve';
|
|
1
2
|
// ============================================================================
|
|
2
3
|
// UTILITY FUNCTIONS
|
|
3
4
|
// ============================================================================
|
|
@@ -79,6 +80,54 @@ function findJunctions(walls) {
|
|
|
79
80
|
}
|
|
80
81
|
return actualJunctions;
|
|
81
82
|
}
|
|
83
|
+
function getWallDirectionFromJunction(wall, endType) {
|
|
84
|
+
if (endType === 'passthrough') {
|
|
85
|
+
return {
|
|
86
|
+
x: wall.end[0] - wall.start[0],
|
|
87
|
+
y: wall.end[1] - wall.start[1],
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
if (isCurvedWall(wall)) {
|
|
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 };
|
|
95
|
+
}
|
|
96
|
+
return endType === 'start'
|
|
97
|
+
? { x: wall.end[0] - wall.start[0], y: wall.end[1] - wall.start[1] }
|
|
98
|
+
: { x: wall.start[0] - wall.end[0], y: wall.start[1] - wall.end[1] };
|
|
99
|
+
}
|
|
100
|
+
function getWallBoundaryFrame(wall, endType) {
|
|
101
|
+
if (isCurvedWall(wall)) {
|
|
102
|
+
const frame = getWallCurveFrameAt(wall, endType === 'start' ? 0 : 1);
|
|
103
|
+
return {
|
|
104
|
+
point: frame.point,
|
|
105
|
+
tangent: endType === 'start'
|
|
106
|
+
? frame.tangent
|
|
107
|
+
: { x: -frame.tangent.x, y: -frame.tangent.y },
|
|
108
|
+
normal: frame.normal,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
const point = endType === 'start'
|
|
112
|
+
? { x: wall.start[0], y: wall.start[1] }
|
|
113
|
+
: { x: wall.end[0], y: wall.end[1] };
|
|
114
|
+
const vector = endType === 'start'
|
|
115
|
+
? { x: wall.end[0] - wall.start[0], y: wall.end[1] - wall.start[1] }
|
|
116
|
+
: { x: wall.start[0] - wall.end[0], y: wall.start[1] - wall.end[1] };
|
|
117
|
+
const length = Math.hypot(vector.x, vector.y);
|
|
118
|
+
if (length < 1e-9) {
|
|
119
|
+
return {
|
|
120
|
+
point,
|
|
121
|
+
tangent: { x: 1, y: 0 },
|
|
122
|
+
normal: { x: 0, y: 1 },
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
point,
|
|
127
|
+
tangent: { x: vector.x / length, y: vector.y / length },
|
|
128
|
+
normal: { x: -vector.y / length, y: vector.x / length },
|
|
129
|
+
};
|
|
130
|
+
}
|
|
82
131
|
function calculateJunctionIntersections(junction, getThickness) {
|
|
83
132
|
const { meetingPoint, connectedWalls } = junction;
|
|
84
133
|
const processedWalls = [];
|
|
@@ -104,9 +153,7 @@ function calculateJunctionIntersections(junction, getThickness) {
|
|
|
104
153
|
}
|
|
105
154
|
else {
|
|
106
155
|
// Normal wall endpoint (start or end)
|
|
107
|
-
const v = endType
|
|
108
|
-
? { x: wall.end[0] - wall.start[0], y: wall.end[1] - wall.start[1] }
|
|
109
|
-
: { x: wall.start[0] - wall.end[0], y: wall.start[1] - wall.end[1] };
|
|
156
|
+
const v = getWallDirectionFromJunction(wall, endType);
|
|
110
157
|
const L = Math.sqrt(v.x * v.x + v.y * v.y);
|
|
111
158
|
if (L < 1e-9)
|
|
112
159
|
continue;
|
|
@@ -169,6 +216,32 @@ export function calculateLevelMiters(walls) {
|
|
|
169
216
|
}
|
|
170
217
|
return { junctionData, junctions };
|
|
171
218
|
}
|
|
219
|
+
export function getWallMiterBoundaryPoints(wall, miterData) {
|
|
220
|
+
const thickness = wall.thickness ?? 0.1;
|
|
221
|
+
const halfThickness = thickness / 2;
|
|
222
|
+
const startFrame = getWallBoundaryFrame(wall, 'start');
|
|
223
|
+
const endFrame = getWallBoundaryFrame(wall, 'end');
|
|
224
|
+
const startJunction = miterData.junctionData.get(pointToKey(startFrame.point))?.get(wall.id);
|
|
225
|
+
const endJunction = miterData.junctionData.get(pointToKey(endFrame.point))?.get(wall.id);
|
|
226
|
+
return {
|
|
227
|
+
startLeft: startJunction?.left ?? {
|
|
228
|
+
x: startFrame.point.x + startFrame.normal.x * halfThickness,
|
|
229
|
+
y: startFrame.point.y + startFrame.normal.y * halfThickness,
|
|
230
|
+
},
|
|
231
|
+
startRight: startJunction?.right ?? {
|
|
232
|
+
x: startFrame.point.x - startFrame.normal.x * halfThickness,
|
|
233
|
+
y: startFrame.point.y - startFrame.normal.y * halfThickness,
|
|
234
|
+
},
|
|
235
|
+
endLeft: endJunction?.right ?? {
|
|
236
|
+
x: endFrame.point.x + endFrame.normal.x * halfThickness,
|
|
237
|
+
y: endFrame.point.y + endFrame.normal.y * halfThickness,
|
|
238
|
+
},
|
|
239
|
+
endRight: endJunction?.left ?? {
|
|
240
|
+
x: endFrame.point.x - endFrame.normal.x * halfThickness,
|
|
241
|
+
y: endFrame.point.y - endFrame.normal.y * halfThickness,
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
}
|
|
172
245
|
/**
|
|
173
246
|
* Gets wall IDs that share junctions with the given walls
|
|
174
247
|
*/
|
|
@@ -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;
|
|
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;AAIhE,OAAO,EAML,KAAK,aAAa,EACnB,MAAM,iBAAiB,CAAA;AAsRxB,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,oFA2GlB"}
|
|
@@ -6,10 +6,202 @@ 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 { getWallCurveFrameAt, getWallSurfacePolygon, isCurvedWall } from './wall-curve';
|
|
9
10
|
import { DEFAULT_WALL_HEIGHT, getWallPlanFootprint, getWallThickness } from './wall-footprint';
|
|
10
|
-
import { calculateLevelMiters, getAdjacentWallIds, } from './wall-mitering';
|
|
11
|
+
import { calculateLevelMiters, getAdjacentWallIds, getWallMiterBoundaryPoints, pointToKey, } from './wall-mitering';
|
|
11
12
|
// Reusable CSG evaluator for better performance
|
|
12
13
|
const csgEvaluator = new Evaluator();
|
|
14
|
+
const CURVED_WALL_3D_ENDPOINT_INSET = 0.0015;
|
|
15
|
+
const WALL_FACE_NORMAL_Y_EPSILON = 0.6;
|
|
16
|
+
const WALL_FACE_EDGE_DISTANCE_EPSILON = 0.003;
|
|
17
|
+
function ensureUv2Attribute(geometry) {
|
|
18
|
+
const uv = geometry.getAttribute('uv');
|
|
19
|
+
if (!uv)
|
|
20
|
+
return;
|
|
21
|
+
geometry.setAttribute('uv2', new THREE.Float32BufferAttribute(Array.from(uv.array), 2));
|
|
22
|
+
}
|
|
23
|
+
function insetCurvedWallBoundaryPointsFor3D(wall, boundaryPoints, miterData) {
|
|
24
|
+
if (!boundaryPoints || !isCurvedWall(wall)) {
|
|
25
|
+
return boundaryPoints;
|
|
26
|
+
}
|
|
27
|
+
const insetDistance = Math.min(CURVED_WALL_3D_ENDPOINT_INSET, Math.max((wall.thickness ?? 0.1) * 0.01, 0.0005));
|
|
28
|
+
if (insetDistance <= 0) {
|
|
29
|
+
return boundaryPoints;
|
|
30
|
+
}
|
|
31
|
+
const next = { ...boundaryPoints };
|
|
32
|
+
const startJunction = miterData.junctions.get(pointToKey({ x: wall.start[0], y: wall.start[1] }));
|
|
33
|
+
const endJunction = miterData.junctions.get(pointToKey({ x: wall.end[0], y: wall.end[1] }));
|
|
34
|
+
if (startJunction && startJunction.connectedWalls.length > 1) {
|
|
35
|
+
const frame = getWallCurveFrameAt(wall, 0);
|
|
36
|
+
next.startLeft = {
|
|
37
|
+
x: next.startLeft.x + frame.tangent.x * insetDistance,
|
|
38
|
+
y: next.startLeft.y + frame.tangent.y * insetDistance,
|
|
39
|
+
};
|
|
40
|
+
next.startRight = {
|
|
41
|
+
x: next.startRight.x + frame.tangent.x * insetDistance,
|
|
42
|
+
y: next.startRight.y + frame.tangent.y * insetDistance,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (endJunction && endJunction.connectedWalls.length > 1) {
|
|
46
|
+
const frame = getWallCurveFrameAt(wall, 1);
|
|
47
|
+
next.endLeft = {
|
|
48
|
+
x: next.endLeft.x - frame.tangent.x * insetDistance,
|
|
49
|
+
y: next.endLeft.y - frame.tangent.y * insetDistance,
|
|
50
|
+
};
|
|
51
|
+
next.endRight = {
|
|
52
|
+
x: next.endRight.x - frame.tangent.x * insetDistance,
|
|
53
|
+
y: next.endRight.y - frame.tangent.y * insetDistance,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return next;
|
|
57
|
+
}
|
|
58
|
+
function addTaggedWallBoundaryEdge(edges, points, startIndex, endIndex, tag) {
|
|
59
|
+
const start = points[startIndex];
|
|
60
|
+
const end = points[endIndex];
|
|
61
|
+
if (!(start && end))
|
|
62
|
+
return;
|
|
63
|
+
if (Math.hypot(end.x - start.x, end.z - start.z) < 1e-6)
|
|
64
|
+
return;
|
|
65
|
+
edges.push({
|
|
66
|
+
start: new THREE.Vector2(start.x, start.z),
|
|
67
|
+
end: new THREE.Vector2(end.x, end.z),
|
|
68
|
+
tag,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
function buildTaggedWallBoundaryEdges(wall, localPoints, miterData) {
|
|
72
|
+
if (localPoints.length < 2)
|
|
73
|
+
return [];
|
|
74
|
+
const edges = [];
|
|
75
|
+
if (isCurvedWall(wall)) {
|
|
76
|
+
const sidePointCount = Math.floor(localPoints.length / 2);
|
|
77
|
+
if (sidePointCount < 2)
|
|
78
|
+
return edges;
|
|
79
|
+
for (let index = 0; index < sidePointCount - 1; index += 1) {
|
|
80
|
+
addTaggedWallBoundaryEdge(edges, localPoints, index, index + 1, 'back');
|
|
81
|
+
}
|
|
82
|
+
addTaggedWallBoundaryEdge(edges, localPoints, sidePointCount - 1, sidePointCount, 'base');
|
|
83
|
+
for (let index = sidePointCount; index < localPoints.length - 1; index += 1) {
|
|
84
|
+
addTaggedWallBoundaryEdge(edges, localPoints, index, index + 1, 'front');
|
|
85
|
+
}
|
|
86
|
+
addTaggedWallBoundaryEdge(edges, localPoints, localPoints.length - 1, 0, 'base');
|
|
87
|
+
return edges;
|
|
88
|
+
}
|
|
89
|
+
const startKey = pointToKey({ x: wall.start[0], y: wall.start[1] });
|
|
90
|
+
const startJunction = miterData.junctionData.get(startKey)?.get(wall.id);
|
|
91
|
+
const startLeftIndex = startJunction ? localPoints.length - 2 : localPoints.length - 1;
|
|
92
|
+
const endLeftIndex = startJunction ? localPoints.length - 3 : localPoints.length - 2;
|
|
93
|
+
addTaggedWallBoundaryEdge(edges, localPoints, 0, 1, 'back');
|
|
94
|
+
for (let index = 1; index < endLeftIndex; index += 1) {
|
|
95
|
+
addTaggedWallBoundaryEdge(edges, localPoints, index, index + 1, 'base');
|
|
96
|
+
}
|
|
97
|
+
addTaggedWallBoundaryEdge(edges, localPoints, endLeftIndex, startLeftIndex, 'front');
|
|
98
|
+
for (let index = startLeftIndex; index < localPoints.length - 1; index += 1) {
|
|
99
|
+
addTaggedWallBoundaryEdge(edges, localPoints, index, index + 1, 'base');
|
|
100
|
+
}
|
|
101
|
+
addTaggedWallBoundaryEdge(edges, localPoints, localPoints.length - 1, 0, 'base');
|
|
102
|
+
return edges;
|
|
103
|
+
}
|
|
104
|
+
function distanceToWallBoundaryEdge(point, edge) {
|
|
105
|
+
const edgeDx = edge.end.x - edge.start.x;
|
|
106
|
+
const edgeDz = edge.end.y - edge.start.y;
|
|
107
|
+
const pointDx = point.x - edge.start.x;
|
|
108
|
+
const pointDz = point.y - edge.start.y;
|
|
109
|
+
const edgeLengthSq = edgeDx * edgeDx + edgeDz * edgeDz;
|
|
110
|
+
if (edgeLengthSq < 1e-12) {
|
|
111
|
+
return point.distanceTo(edge.start);
|
|
112
|
+
}
|
|
113
|
+
const t = THREE.MathUtils.clamp((pointDx * edgeDx + pointDz * edgeDz) / edgeLengthSq, 0, 1);
|
|
114
|
+
const closestX = edge.start.x + edgeDx * t;
|
|
115
|
+
const closestZ = edge.start.y + edgeDz * t;
|
|
116
|
+
return Math.hypot(point.x - closestX, point.y - closestZ);
|
|
117
|
+
}
|
|
118
|
+
function getWallFaceMaterialIndex(wall, face) {
|
|
119
|
+
const semantic = face === 'front' ? wall.frontSide : wall.backSide;
|
|
120
|
+
const fallback = face === 'front' ? 1 : 2;
|
|
121
|
+
if (semantic === 'interior')
|
|
122
|
+
return 1;
|
|
123
|
+
if (semantic === 'exterior')
|
|
124
|
+
return 2;
|
|
125
|
+
return fallback;
|
|
126
|
+
}
|
|
127
|
+
function assignWallMaterialGroups(geometry, wall, boundaryEdges) {
|
|
128
|
+
const position = geometry.getAttribute('position');
|
|
129
|
+
if (!position)
|
|
130
|
+
return;
|
|
131
|
+
const index = geometry.getIndex();
|
|
132
|
+
const triangleCount = index ? Math.floor(index.count / 3) : Math.floor(position.count / 3);
|
|
133
|
+
if (triangleCount === 0) {
|
|
134
|
+
geometry.clearGroups();
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const triangleMaterials = new Array(triangleCount).fill(0);
|
|
138
|
+
const a = new THREE.Vector3();
|
|
139
|
+
const b = new THREE.Vector3();
|
|
140
|
+
const c = new THREE.Vector3();
|
|
141
|
+
const ab = new THREE.Vector3();
|
|
142
|
+
const ac = new THREE.Vector3();
|
|
143
|
+
const normal = new THREE.Vector3();
|
|
144
|
+
const centroid = new THREE.Vector3();
|
|
145
|
+
const projectedCentroid = new THREE.Vector2();
|
|
146
|
+
const maxBoundaryDistance = Math.max(getWallThickness(wall) * 0.02, WALL_FACE_EDGE_DISTANCE_EPSILON);
|
|
147
|
+
for (let triangleIndex = 0; triangleIndex < triangleCount; triangleIndex += 1) {
|
|
148
|
+
const baseIndex = triangleIndex * 3;
|
|
149
|
+
const ia = index ? index.getX(baseIndex) : baseIndex;
|
|
150
|
+
const ib = index ? index.getX(baseIndex + 1) : baseIndex + 1;
|
|
151
|
+
const ic = index ? index.getX(baseIndex + 2) : baseIndex + 2;
|
|
152
|
+
a.fromBufferAttribute(position, ia);
|
|
153
|
+
b.fromBufferAttribute(position, ib);
|
|
154
|
+
c.fromBufferAttribute(position, ic);
|
|
155
|
+
ab.subVectors(b, a);
|
|
156
|
+
ac.subVectors(c, a);
|
|
157
|
+
normal.crossVectors(ab, ac);
|
|
158
|
+
if (normal.lengthSq() < 1e-12) {
|
|
159
|
+
triangleMaterials[triangleIndex] = 0;
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
normal.normalize();
|
|
163
|
+
if (Math.abs(normal.y) >= WALL_FACE_NORMAL_Y_EPSILON) {
|
|
164
|
+
triangleMaterials[triangleIndex] = 0;
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
centroid
|
|
168
|
+
.copy(a)
|
|
169
|
+
.add(b)
|
|
170
|
+
.add(c)
|
|
171
|
+
.multiplyScalar(1 / 3);
|
|
172
|
+
projectedCentroid.set(centroid.x, centroid.z);
|
|
173
|
+
let nearestTag = null;
|
|
174
|
+
let nearestDistance = Number.POSITIVE_INFINITY;
|
|
175
|
+
for (const edge of boundaryEdges) {
|
|
176
|
+
const distance = distanceToWallBoundaryEdge(projectedCentroid, edge);
|
|
177
|
+
if (distance < nearestDistance) {
|
|
178
|
+
nearestDistance = distance;
|
|
179
|
+
nearestTag = edge.tag;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (!nearestTag || nearestDistance > maxBoundaryDistance) {
|
|
183
|
+
triangleMaterials[triangleIndex] = 0;
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
if (nearestTag === 'base') {
|
|
187
|
+
triangleMaterials[triangleIndex] = 0;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
triangleMaterials[triangleIndex] = getWallFaceMaterialIndex(wall, nearestTag);
|
|
191
|
+
}
|
|
192
|
+
geometry.clearGroups();
|
|
193
|
+
let currentMaterial = triangleMaterials[0] ?? 0;
|
|
194
|
+
let groupStart = 0;
|
|
195
|
+
for (let triangleIndex = 1; triangleIndex < triangleCount; triangleIndex += 1) {
|
|
196
|
+
const materialIndex = triangleMaterials[triangleIndex] ?? 0;
|
|
197
|
+
if (materialIndex === currentMaterial)
|
|
198
|
+
continue;
|
|
199
|
+
geometry.addGroup(groupStart * 3, (triangleIndex - groupStart) * 3, currentMaterial);
|
|
200
|
+
groupStart = triangleIndex;
|
|
201
|
+
currentMaterial = materialIndex;
|
|
202
|
+
}
|
|
203
|
+
geometry.addGroup(groupStart * 3, (triangleCount - groupStart) * 3, currentMaterial);
|
|
204
|
+
}
|
|
13
205
|
// ============================================================================
|
|
14
206
|
// WALL SYSTEM
|
|
15
207
|
// ============================================================================
|
|
@@ -131,7 +323,10 @@ export function generateExtrudedWall(wallNode, childrenNodes, miterData, slabEle
|
|
|
131
323
|
if (L < 1e-9) {
|
|
132
324
|
return new THREE.BufferGeometry();
|
|
133
325
|
}
|
|
134
|
-
const
|
|
326
|
+
const boundaryPoints = getWallMiterBoundaryPoints(wallNode, miterData);
|
|
327
|
+
const polyPoints = isCurvedWall(wallNode)
|
|
328
|
+
? getWallSurfacePolygon(wallNode, 24, insetCurvedWallBoundaryPointsFor3D(wallNode, boundaryPoints, miterData) ?? undefined)
|
|
329
|
+
: getWallPlanFootprint(wallNode, miterData);
|
|
135
330
|
if (polyPoints.length < 3) {
|
|
136
331
|
return new THREE.BufferGeometry();
|
|
137
332
|
}
|
|
@@ -150,6 +345,7 @@ export function generateExtrudedWall(wallNode, childrenNodes, miterData, slabEle
|
|
|
150
345
|
};
|
|
151
346
|
// Convert polygon to local coordinates
|
|
152
347
|
const localPoints = polyPoints.map(worldToLocal);
|
|
348
|
+
const boundaryEdges = buildTaggedWallBoundaryEdges(wallNode, localPoints, miterData);
|
|
153
349
|
// Build THREE.js shape
|
|
154
350
|
// Shape uses (x, y) where we map: shape.x = local.x, shape.y = -local.z
|
|
155
351
|
// The negation is needed because after rotateX(-PI/2), shape.y becomes -geometry.z
|
|
@@ -167,6 +363,8 @@ export function generateExtrudedWall(wallNode, childrenNodes, miterData, slabEle
|
|
|
167
363
|
// Rotate so extrusion direction (Z) becomes height direction (Y)
|
|
168
364
|
geometry.rotateX(-Math.PI / 2);
|
|
169
365
|
geometry.computeVertexNormals();
|
|
366
|
+
assignWallMaterialGroups(geometry, wallNode, boundaryEdges);
|
|
367
|
+
ensureUv2Attribute(geometry);
|
|
170
368
|
// Apply CSG subtraction for cutouts (doors/windows)
|
|
171
369
|
const cutoutBrushes = collectCutoutBrushes(wallNode, childrenNodes, thickness);
|
|
172
370
|
if (cutoutBrushes.length === 0) {
|
|
@@ -195,6 +393,8 @@ export function generateExtrudedWall(wallNode, childrenNodes, miterData, slabEle
|
|
|
195
393
|
}
|
|
196
394
|
const resultGeometry = resultBrush.geometry;
|
|
197
395
|
resultGeometry.computeVertexNormals();
|
|
396
|
+
assignWallMaterialGroups(resultGeometry, wallNode, boundaryEdges);
|
|
397
|
+
ensureUv2Attribute(resultGeometry);
|
|
198
398
|
return resultGeometry;
|
|
199
399
|
}
|
|
200
400
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pascal-app/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Core library for Pascal 3D building editor",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"@react-three/drei": "^10",
|
|
31
31
|
"@react-three/fiber": "^9",
|
|
32
32
|
"react": "^18 || ^19",
|
|
33
|
-
"three": "^0.
|
|
33
|
+
"three": "^0.184"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"dedent": "^1.7.1",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"@pascal/typescript-config": "*",
|
|
48
48
|
"@types/react": "^19.2.2",
|
|
49
49
|
"typescript": "5.9.3",
|
|
50
|
-
"@types/three": "^0.
|
|
50
|
+
"@types/three": "^0.184.0"
|
|
51
51
|
},
|
|
52
52
|
"keywords": [
|
|
53
53
|
"3d",
|