@pascal-app/core 0.3.2 → 0.4.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 +4 -2
- package/dist/events/bus.d.ts.map +1 -1
- package/dist/hooks/scene-registry/scene-registry.d.ts +2 -0
- package/dist/hooks/scene-registry/scene-registry.d.ts.map +1 -1
- package/dist/hooks/scene-registry/scene-registry.js +3 -0
- 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 +11 -3
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -8
- package/dist/materials.d.ts +10 -0
- package/dist/materials.d.ts.map +1 -0
- package/dist/materials.js +22 -0
- package/dist/schema/index.d.ts +3 -1
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +3 -1
- 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/stair-segment.d.ts +81 -0
- package/dist/schema/nodes/stair-segment.d.ts.map +1 -0
- package/dist/schema/nodes/stair-segment.js +42 -0
- package/dist/schema/nodes/stair.d.ts +56 -0
- package/dist/schema/nodes/stair.d.ts.map +1 -0
- package/dist/schema/nodes/stair.js +22 -0
- package/dist/schema/types.d.ts +119 -1
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/types.js +4 -0
- package/dist/store/actions/node-actions.d.ts.map +1 -1
- package/dist/store/actions/node-actions.js +25 -29
- package/dist/store/use-live-transforms.d.ts +14 -0
- package/dist/store/use-live-transforms.d.ts.map +1 -0
- package/dist/store/use-live-transforms.js +20 -0
- package/dist/store/use-scene.d.ts +2 -5
- package/dist/store/use-scene.d.ts.map +1 -1
- package/dist/store/use-scene.js +25 -15
- package/dist/systems/door/door-system.d.ts.map +1 -1
- package/dist/systems/door/door-system.js +1 -17
- package/dist/systems/roof/roof-system.d.ts.map +1 -1
- package/dist/systems/roof/roof-system.js +18 -0
- package/dist/systems/slab/slab-system.d.ts.map +1 -1
- package/dist/systems/slab/slab-system.js +71 -26
- package/dist/systems/stair/stair-system.d.ts +2 -0
- package/dist/systems/stair/stair-system.d.ts.map +1 -0
- package/dist/systems/stair/stair-system.js +354 -0
- package/dist/systems/wall/wall-system.d.ts.map +1 -1
- package/dist/systems/wall/wall-system.js +2 -0
- package/dist/systems/window/window-system.d.ts.map +1 -1
- package/dist/systems/window/window-system.js +8 -24
- package/dist/utils/clone-scene-graph.d.ts +25 -1
- package/dist/utils/clone-scene-graph.d.ts.map +1 -1
- package/dist/utils/clone-scene-graph.js +160 -5
- package/package.json +6 -1
package/dist/schema/types.d.ts
CHANGED
|
@@ -175,7 +175,7 @@ export declare const AnyNode: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
175
175
|
metadata: z.ZodDefault<z.ZodOptional<z.ZodJSONSchema>>;
|
|
176
176
|
id: z.ZodDefault<z.ZodTemplateLiteral<`level_${string}`>>;
|
|
177
177
|
type: z.ZodDefault<z.ZodLiteral<"level">>;
|
|
178
|
-
children: z.ZodDefault<z.ZodArray<z.ZodUnion<readonly [z.ZodDefault<z.ZodTemplateLiteral<`wall_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`zone_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`slab_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`ceiling_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`roof_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`scan_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`guide_${string}`>>]>>>;
|
|
178
|
+
children: z.ZodDefault<z.ZodArray<z.ZodUnion<readonly [z.ZodDefault<z.ZodTemplateLiteral<`wall_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`zone_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`slab_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`ceiling_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`roof_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`stair_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`scan_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`guide_${string}`>>]>>>;
|
|
179
179
|
level: z.ZodDefault<z.ZodNumber>;
|
|
180
180
|
}, z.core.$strip>, z.ZodObject<{
|
|
181
181
|
object: z.ZodDefault<z.ZodLiteral<"node">>;
|
|
@@ -579,6 +579,124 @@ export declare const AnyNode: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
579
579
|
deckThickness: z.ZodDefault<z.ZodNumber>;
|
|
580
580
|
overhang: z.ZodDefault<z.ZodNumber>;
|
|
581
581
|
shingleThickness: z.ZodDefault<z.ZodNumber>;
|
|
582
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
583
|
+
object: z.ZodDefault<z.ZodLiteral<"node">>;
|
|
584
|
+
name: z.ZodOptional<z.ZodString>;
|
|
585
|
+
parentId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
586
|
+
visible: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
587
|
+
camera: z.ZodOptional<z.ZodObject<{
|
|
588
|
+
position: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
|
|
589
|
+
target: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
|
|
590
|
+
mode: z.ZodDefault<z.ZodEnum<{
|
|
591
|
+
perspective: "perspective";
|
|
592
|
+
orthographic: "orthographic";
|
|
593
|
+
}>>;
|
|
594
|
+
fov: z.ZodOptional<z.ZodNumber>;
|
|
595
|
+
zoom: z.ZodOptional<z.ZodNumber>;
|
|
596
|
+
}, z.core.$strip>>;
|
|
597
|
+
metadata: z.ZodDefault<z.ZodOptional<z.ZodJSONSchema>>;
|
|
598
|
+
id: z.ZodDefault<z.ZodTemplateLiteral<`stair_${string}`>>;
|
|
599
|
+
type: z.ZodDefault<z.ZodLiteral<"stair">>;
|
|
600
|
+
material: z.ZodOptional<z.ZodObject<{
|
|
601
|
+
preset: z.ZodOptional<z.ZodEnum<{
|
|
602
|
+
custom: "custom";
|
|
603
|
+
white: "white";
|
|
604
|
+
brick: "brick";
|
|
605
|
+
concrete: "concrete";
|
|
606
|
+
wood: "wood";
|
|
607
|
+
glass: "glass";
|
|
608
|
+
metal: "metal";
|
|
609
|
+
plaster: "plaster";
|
|
610
|
+
tile: "tile";
|
|
611
|
+
marble: "marble";
|
|
612
|
+
}>>;
|
|
613
|
+
properties: z.ZodOptional<z.ZodObject<{
|
|
614
|
+
color: z.ZodDefault<z.ZodString>;
|
|
615
|
+
roughness: z.ZodDefault<z.ZodNumber>;
|
|
616
|
+
metalness: z.ZodDefault<z.ZodNumber>;
|
|
617
|
+
opacity: z.ZodDefault<z.ZodNumber>;
|
|
618
|
+
transparent: z.ZodDefault<z.ZodBoolean>;
|
|
619
|
+
side: z.ZodDefault<z.ZodEnum<{
|
|
620
|
+
front: "front";
|
|
621
|
+
back: "back";
|
|
622
|
+
double: "double";
|
|
623
|
+
}>>;
|
|
624
|
+
}, z.core.$strip>>;
|
|
625
|
+
texture: z.ZodOptional<z.ZodObject<{
|
|
626
|
+
url: z.ZodString;
|
|
627
|
+
repeat: z.ZodOptional<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>>;
|
|
628
|
+
scale: z.ZodOptional<z.ZodNumber>;
|
|
629
|
+
}, z.core.$strip>>;
|
|
630
|
+
}, z.core.$strip>>;
|
|
631
|
+
position: z.ZodDefault<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
|
|
632
|
+
rotation: z.ZodDefault<z.ZodNumber>;
|
|
633
|
+
children: z.ZodDefault<z.ZodArray<z.ZodDefault<z.ZodTemplateLiteral<`sseg_${string}`>>>>;
|
|
634
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
635
|
+
object: z.ZodDefault<z.ZodLiteral<"node">>;
|
|
636
|
+
name: z.ZodOptional<z.ZodString>;
|
|
637
|
+
parentId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
638
|
+
visible: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
639
|
+
camera: z.ZodOptional<z.ZodObject<{
|
|
640
|
+
position: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
|
|
641
|
+
target: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
|
|
642
|
+
mode: z.ZodDefault<z.ZodEnum<{
|
|
643
|
+
perspective: "perspective";
|
|
644
|
+
orthographic: "orthographic";
|
|
645
|
+
}>>;
|
|
646
|
+
fov: z.ZodOptional<z.ZodNumber>;
|
|
647
|
+
zoom: z.ZodOptional<z.ZodNumber>;
|
|
648
|
+
}, z.core.$strip>>;
|
|
649
|
+
metadata: z.ZodDefault<z.ZodOptional<z.ZodJSONSchema>>;
|
|
650
|
+
id: z.ZodDefault<z.ZodTemplateLiteral<`sseg_${string}`>>;
|
|
651
|
+
type: z.ZodDefault<z.ZodLiteral<"stair-segment">>;
|
|
652
|
+
material: z.ZodOptional<z.ZodObject<{
|
|
653
|
+
preset: z.ZodOptional<z.ZodEnum<{
|
|
654
|
+
custom: "custom";
|
|
655
|
+
white: "white";
|
|
656
|
+
brick: "brick";
|
|
657
|
+
concrete: "concrete";
|
|
658
|
+
wood: "wood";
|
|
659
|
+
glass: "glass";
|
|
660
|
+
metal: "metal";
|
|
661
|
+
plaster: "plaster";
|
|
662
|
+
tile: "tile";
|
|
663
|
+
marble: "marble";
|
|
664
|
+
}>>;
|
|
665
|
+
properties: z.ZodOptional<z.ZodObject<{
|
|
666
|
+
color: z.ZodDefault<z.ZodString>;
|
|
667
|
+
roughness: z.ZodDefault<z.ZodNumber>;
|
|
668
|
+
metalness: z.ZodDefault<z.ZodNumber>;
|
|
669
|
+
opacity: z.ZodDefault<z.ZodNumber>;
|
|
670
|
+
transparent: z.ZodDefault<z.ZodBoolean>;
|
|
671
|
+
side: z.ZodDefault<z.ZodEnum<{
|
|
672
|
+
front: "front";
|
|
673
|
+
back: "back";
|
|
674
|
+
double: "double";
|
|
675
|
+
}>>;
|
|
676
|
+
}, z.core.$strip>>;
|
|
677
|
+
texture: z.ZodOptional<z.ZodObject<{
|
|
678
|
+
url: z.ZodString;
|
|
679
|
+
repeat: z.ZodOptional<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>>;
|
|
680
|
+
scale: z.ZodOptional<z.ZodNumber>;
|
|
681
|
+
}, z.core.$strip>>;
|
|
682
|
+
}, z.core.$strip>>;
|
|
683
|
+
position: z.ZodDefault<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
|
|
684
|
+
rotation: z.ZodDefault<z.ZodNumber>;
|
|
685
|
+
segmentType: z.ZodDefault<z.ZodEnum<{
|
|
686
|
+
stair: "stair";
|
|
687
|
+
landing: "landing";
|
|
688
|
+
}>>;
|
|
689
|
+
width: z.ZodDefault<z.ZodNumber>;
|
|
690
|
+
length: z.ZodDefault<z.ZodNumber>;
|
|
691
|
+
height: z.ZodDefault<z.ZodNumber>;
|
|
692
|
+
stepCount: z.ZodDefault<z.ZodNumber>;
|
|
693
|
+
attachmentSide: z.ZodDefault<z.ZodEnum<{
|
|
694
|
+
front: "front";
|
|
695
|
+
left: "left";
|
|
696
|
+
right: "right";
|
|
697
|
+
}>>;
|
|
698
|
+
fillToFloor: z.ZodDefault<z.ZodBoolean>;
|
|
699
|
+
thickness: z.ZodDefault<z.ZodNumber>;
|
|
582
700
|
}, z.core.$strip>, z.ZodObject<{
|
|
583
701
|
object: z.ZodDefault<z.ZodLiteral<"node">>;
|
|
584
702
|
name: z.ZodOptional<z.ZodString>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/schema/types.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,KAAK,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/schema/types.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,KAAK,CAAA;AAkBnB,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAiBlB,CAAA;AAEF,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,OAAO,CAAC,CAAA;AAC7C,MAAM,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;AACzC,MAAM,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA"}
|
package/dist/schema/types.js
CHANGED
|
@@ -10,6 +10,8 @@ import { RoofSegmentNode } from './nodes/roof-segment';
|
|
|
10
10
|
import { ScanNode } from './nodes/scan';
|
|
11
11
|
import { SiteNode } from './nodes/site';
|
|
12
12
|
import { SlabNode } from './nodes/slab';
|
|
13
|
+
import { StairNode } from './nodes/stair';
|
|
14
|
+
import { StairSegmentNode } from './nodes/stair-segment';
|
|
13
15
|
import { WallNode } from './nodes/wall';
|
|
14
16
|
import { WindowNode } from './nodes/window';
|
|
15
17
|
import { ZoneNode } from './nodes/zone';
|
|
@@ -24,6 +26,8 @@ export const AnyNode = z.discriminatedUnion('type', [
|
|
|
24
26
|
CeilingNode,
|
|
25
27
|
RoofNode,
|
|
26
28
|
RoofSegmentNode,
|
|
29
|
+
StairNode,
|
|
30
|
+
StairSegmentNode,
|
|
27
31
|
ScanNode,
|
|
28
32
|
GuideNode,
|
|
29
33
|
WindowNode,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node-actions.d.ts","sourceRoot":"","sources":["../../../src/store/actions/node-actions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAEtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAQ9C,eAAO,MAAM,iBAAiB,GAC5B,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,UAAU,CAAC,KAAK,IAAI,EAC7D,KAAK,MAAM,UAAU,EACrB,KAAK;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,EAAE,
|
|
1
|
+
{"version":3,"file":"node-actions.d.ts","sourceRoot":"","sources":["../../../src/store/actions/node-actions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAEtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAQ9C,eAAO,MAAM,iBAAiB,GAC5B,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,UAAU,CAAC,KAAK,IAAI,EAC7D,KAAK,MAAM,UAAU,EACrB,KAAK;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,EAAE,SA4C/C,CAAA;AAED,eAAO,MAAM,iBAAiB,GAC5B,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,UAAU,CAAC,KAAK,IAAI,EAC7D,KAAK,MAAM,UAAU,EACrB,SAAS;IAAE,EAAE,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;CAAE,EAAE,SA+DrD,CAAA;AAED,eAAO,MAAM,iBAAiB,GAC5B,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,UAAU,CAAC,KAAK,IAAI,EAC7D,KAAK,MAAM,UAAU,EACrB,KAAK,SAAS,EAAE,SAuEjB,CAAA"}
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
let pendingRafId = null;
|
|
3
3
|
let pendingUpdates = new Set();
|
|
4
4
|
export const createNodesAction = (set, get, ops) => {
|
|
5
|
+
if (get().readOnly)
|
|
6
|
+
return;
|
|
5
7
|
set((state) => {
|
|
6
8
|
const nextNodes = { ...state.nodes };
|
|
7
9
|
const nextRootIds = [...state.rootNodeIds];
|
|
@@ -41,8 +43,9 @@ export const createNodesAction = (set, get, ops) => {
|
|
|
41
43
|
});
|
|
42
44
|
};
|
|
43
45
|
export const updateNodesAction = (set, get, updates) => {
|
|
46
|
+
if (get().readOnly)
|
|
47
|
+
return;
|
|
44
48
|
const parentsToUpdate = new Set();
|
|
45
|
-
const idsToMarkDirty = new Set();
|
|
46
49
|
set((state) => {
|
|
47
50
|
const nextNodes = { ...state.nodes };
|
|
48
51
|
for (const { id, data } of updates) {
|
|
@@ -77,23 +80,17 @@ export const updateNodesAction = (set, get, updates) => {
|
|
|
77
80
|
}
|
|
78
81
|
return { nodes: nextNodes };
|
|
79
82
|
});
|
|
80
|
-
//
|
|
83
|
+
// Batch dirty-marking into a single RAF to avoid redundant callbacks during rapid updates
|
|
81
84
|
for (const u of updates) {
|
|
82
|
-
|
|
85
|
+
pendingUpdates.add(u.id);
|
|
83
86
|
}
|
|
84
87
|
for (const pId of parentsToUpdate) {
|
|
85
|
-
|
|
88
|
+
pendingUpdates.add(pId);
|
|
86
89
|
}
|
|
87
|
-
// Add to pending updates set
|
|
88
|
-
for (const id of idsToMarkDirty) {
|
|
89
|
-
pendingUpdates.add(id);
|
|
90
|
-
}
|
|
91
|
-
// Cancel any pending RAF and schedule a new one
|
|
92
90
|
if (pendingRafId !== null) {
|
|
93
91
|
cancelAnimationFrame(pendingRafId);
|
|
94
92
|
}
|
|
95
93
|
pendingRafId = requestAnimationFrame(() => {
|
|
96
|
-
// Mark all pending updates as dirty
|
|
97
94
|
pendingUpdates.forEach((id) => {
|
|
98
95
|
get().markDirty(id);
|
|
99
96
|
});
|
|
@@ -102,36 +99,35 @@ export const updateNodesAction = (set, get, updates) => {
|
|
|
102
99
|
});
|
|
103
100
|
};
|
|
104
101
|
export const deleteNodesAction = (set, get, ids) => {
|
|
102
|
+
if (get().readOnly)
|
|
103
|
+
return;
|
|
105
104
|
const parentsToMarkDirty = new Set();
|
|
106
105
|
set((state) => {
|
|
107
106
|
const nextNodes = { ...state.nodes };
|
|
108
107
|
const nextCollections = { ...state.collections };
|
|
109
108
|
let nextRootIds = [...state.rootNodeIds];
|
|
110
|
-
// Collect all
|
|
111
|
-
//
|
|
112
|
-
const
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
if (!node)
|
|
109
|
+
// Collect all ids to delete (the requested ids + all their descendants) before
|
|
110
|
+
// mutating anything, so the recursive walk reads consistent state.
|
|
111
|
+
const allIds = new Set();
|
|
112
|
+
const collect = (id) => {
|
|
113
|
+
if (allIds.has(id))
|
|
116
114
|
return;
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
115
|
+
allIds.add(id);
|
|
116
|
+
const node = nextNodes[id];
|
|
117
|
+
if (node && 'children' in node) {
|
|
118
|
+
for (const cid of node.children)
|
|
119
|
+
collect(cid);
|
|
122
120
|
}
|
|
123
121
|
};
|
|
124
|
-
for (const id of ids)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
// Now process all nodes for deletion
|
|
128
|
-
for (const id of allIdsToDelete) {
|
|
122
|
+
for (const id of ids)
|
|
123
|
+
collect(id);
|
|
124
|
+
for (const id of allIds) {
|
|
129
125
|
const node = nextNodes[id];
|
|
130
126
|
if (!node)
|
|
131
127
|
continue;
|
|
132
|
-
// 1. Remove reference from
|
|
128
|
+
// 1. Remove reference from parent — only if the parent itself is NOT also being deleted
|
|
133
129
|
const parentId = node.parentId;
|
|
134
|
-
if (parentId && nextNodes[parentId]) {
|
|
130
|
+
if (parentId && nextNodes[parentId] && !allIds.has(parentId)) {
|
|
135
131
|
const parent = nextNodes[parentId];
|
|
136
132
|
if (parent.children) {
|
|
137
133
|
nextNodes[parent.id] = {
|
|
@@ -141,7 +137,7 @@ export const deleteNodesAction = (set, get, ids) => {
|
|
|
141
137
|
parentsToMarkDirty.add(parent.id);
|
|
142
138
|
}
|
|
143
139
|
}
|
|
144
|
-
// 2. Remove from
|
|
140
|
+
// 2. Remove from root list
|
|
145
141
|
nextRootIds = nextRootIds.filter((rid) => rid !== id);
|
|
146
142
|
// 3. Remove from any collections it belongs to
|
|
147
143
|
if ('collectionIds' in node && node.collectionIds) {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type LiveTransform = {
|
|
2
|
+
position: [number, number, number];
|
|
3
|
+
rotation: number;
|
|
4
|
+
};
|
|
5
|
+
type LiveTransformState = {
|
|
6
|
+
transforms: Map<string, LiveTransform>;
|
|
7
|
+
set(nodeId: string, transform: LiveTransform): void;
|
|
8
|
+
get(nodeId: string): LiveTransform | undefined;
|
|
9
|
+
clear(nodeId: string): void;
|
|
10
|
+
clearAll(): void;
|
|
11
|
+
};
|
|
12
|
+
declare const useLiveTransforms: import("zustand").UseBoundStore<import("zustand").StoreApi<LiveTransformState>>;
|
|
13
|
+
export default useLiveTransforms;
|
|
14
|
+
//# sourceMappingURL=use-live-transforms.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-live-transforms.d.ts","sourceRoot":"","sources":["../../src/store/use-live-transforms.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAClC,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,KAAK,kBAAkB,GAAG;IACxB,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;IACtC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,GAAG,IAAI,CAAA;IACnD,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAAA;IAC9C,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,QAAQ,IAAI,IAAI,CAAA;CACjB,CAAA;AAED,QAAA,MAAM,iBAAiB,iFAgBpB,CAAA;AAEH,eAAe,iBAAiB,CAAA"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Ephemeral live transform state for nodes being actively dragged/moved.
|
|
2
|
+
// This decouples 2D (floorplan) and 3D (viewer) so neither needs to peek
|
|
3
|
+
// into the other's scene graph during drag operations.
|
|
4
|
+
import { create } from 'zustand';
|
|
5
|
+
const useLiveTransforms = create((set, get) => ({
|
|
6
|
+
transforms: new Map(),
|
|
7
|
+
set: (nodeId, transform) => set((state) => {
|
|
8
|
+
const next = new Map(state.transforms);
|
|
9
|
+
next.set(nodeId, transform);
|
|
10
|
+
return { transforms: next };
|
|
11
|
+
}),
|
|
12
|
+
get: (nodeId) => get().transforms.get(nodeId),
|
|
13
|
+
clear: (nodeId) => set((state) => {
|
|
14
|
+
const next = new Map(state.transforms);
|
|
15
|
+
next.delete(nodeId);
|
|
16
|
+
return { transforms: next };
|
|
17
|
+
}),
|
|
18
|
+
clearAll: () => set({ transforms: new Map() }),
|
|
19
|
+
}));
|
|
20
|
+
export default useLiveTransforms;
|
|
@@ -7,6 +7,8 @@ export type SceneState = {
|
|
|
7
7
|
rootNodeIds: AnyNodeId[];
|
|
8
8
|
dirtyNodes: Set<AnyNodeId>;
|
|
9
9
|
collections: Record<CollectionId, Collection>;
|
|
10
|
+
readOnly: boolean;
|
|
11
|
+
setReadOnly: (readOnly: boolean) => void;
|
|
10
12
|
loadScene: () => void;
|
|
11
13
|
clearScene: () => void;
|
|
12
14
|
unloadScene: () => void;
|
|
@@ -36,10 +38,5 @@ type UseSceneStore = UseBoundStore<StoreApi<SceneState>> & {
|
|
|
36
38
|
};
|
|
37
39
|
declare const useScene: UseSceneStore;
|
|
38
40
|
export default useScene;
|
|
39
|
-
/**
|
|
40
|
-
* Clears temporal history tracking variables to prevent memory leaks.
|
|
41
|
-
* Should be called when unloading a scene to release node references.
|
|
42
|
-
*/
|
|
43
|
-
export declare function clearTemporalTracking(): void;
|
|
44
41
|
export declare function clearSceneHistory(): void;
|
|
45
42
|
//# sourceMappingURL=use-scene.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-scene.d.ts","sourceRoot":"","sources":["../../src/store/use-scene.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAE1C,OAAO,EAAU,KAAK,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,SAAS,CAAA;AAEnE,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAIrE,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AA8CzD,MAAM,MAAM,UAAU,GAAG;IAEvB,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;IAGjC,WAAW,EAAE,SAAS,EAAE,CAAA;IAGxB,UAAU,EAAE,GAAG,CAAC,SAAS,CAAC,CAAA;IAG1B,WAAW,EAAE,MAAM,CAAC,YAAY,EAAE,UAAU,CAAC,CAAA;IAG7C,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,UAAU,EAAE,MAAM,IAAI,CAAA;IACtB,WAAW,EAAE,MAAM,IAAI,CAAA;IACvB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,WAAW,EAAE,SAAS,EAAE,KAAK,IAAI,CAAA;IAE/E,SAAS,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,IAAI,CAAA;IAClC,UAAU,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,IAAI,CAAA;IAEnC,UAAU,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,SAAS,KAAK,IAAI,CAAA;IACzD,WAAW,EAAE,CAAC,GAAG,EAAE;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;KAAE,EAAE,KAAK,IAAI,CAAA;IAErE,UAAU,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,CAAA;IAC3D,WAAW,EAAE,CAAC,OAAO,EAAE;QAAE,EAAE,EAAE,SAAS,CAAC;QAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;KAAE,EAAE,KAAK,IAAI,CAAA;IAE3E,UAAU,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,IAAI,CAAA;IACnC,WAAW,EAAE,CAAC,GAAG,EAAE,SAAS,EAAE,KAAK,IAAI,CAAA;IAGvC,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,KAAK,YAAY,CAAA;IACvE,gBAAgB,EAAE,CAAC,EAAE,EAAE,YAAY,KAAK,IAAI,CAAA;IAC5C,gBAAgB,EAAE,CAAC,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,KAAK,IAAI,CAAA;IACnF,eAAe,EAAE,CAAC,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,KAAK,IAAI,CAAA;IAC9D,oBAAoB,EAAE,CAAC,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,KAAK,IAAI,CAAA;CACpE,CAAA;AAID,KAAK,aAAa,GAAG,aAAa,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,GAAG;IACzD,QAAQ,EAAE,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,GAAG,aAAa,GAAG,aAAa,CAAC,CAAC,CAAC,CAAA;CAC7F,CAAA;AAED,QAAA,MAAM,QAAQ,EAAE,
|
|
1
|
+
{"version":3,"file":"use-scene.d.ts","sourceRoot":"","sources":["../../src/store/use-scene.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAE1C,OAAO,EAAU,KAAK,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,SAAS,CAAA;AAEnE,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAIrE,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AA8CzD,MAAM,MAAM,UAAU,GAAG;IAEvB,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;IAGjC,WAAW,EAAE,SAAS,EAAE,CAAA;IAGxB,UAAU,EAAE,GAAG,CAAC,SAAS,CAAC,CAAA;IAG1B,WAAW,EAAE,MAAM,CAAC,YAAY,EAAE,UAAU,CAAC,CAAA;IAG7C,QAAQ,EAAE,OAAO,CAAA;IACjB,WAAW,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAA;IAGxC,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,UAAU,EAAE,MAAM,IAAI,CAAA;IACtB,WAAW,EAAE,MAAM,IAAI,CAAA;IACvB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,WAAW,EAAE,SAAS,EAAE,KAAK,IAAI,CAAA;IAE/E,SAAS,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,IAAI,CAAA;IAClC,UAAU,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,IAAI,CAAA;IAEnC,UAAU,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,SAAS,KAAK,IAAI,CAAA;IACzD,WAAW,EAAE,CAAC,GAAG,EAAE;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;KAAE,EAAE,KAAK,IAAI,CAAA;IAErE,UAAU,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,CAAA;IAC3D,WAAW,EAAE,CAAC,OAAO,EAAE;QAAE,EAAE,EAAE,SAAS,CAAC;QAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;KAAE,EAAE,KAAK,IAAI,CAAA;IAE3E,UAAU,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,IAAI,CAAA;IACnC,WAAW,EAAE,CAAC,GAAG,EAAE,SAAS,EAAE,KAAK,IAAI,CAAA;IAGvC,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,KAAK,YAAY,CAAA;IACvE,gBAAgB,EAAE,CAAC,EAAE,EAAE,YAAY,KAAK,IAAI,CAAA;IAC5C,gBAAgB,EAAE,CAAC,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,KAAK,IAAI,CAAA;IACnF,eAAe,EAAE,CAAC,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,KAAK,IAAI,CAAA;IAC9D,oBAAoB,EAAE,CAAC,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,KAAK,IAAI,CAAA;CACpE,CAAA;AAID,KAAK,aAAa,GAAG,aAAa,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,GAAG;IACzD,QAAQ,EAAE,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,GAAG,aAAa,GAAG,aAAa,CAAC,CAAC,CAAC,CAAA;CAC7F,CAAA;AAED,QAAA,MAAM,QAAQ,EAAE,aA8Nf,CAAA;AAED,eAAe,QAAQ,CAAA;AAOvB,wBAAgB,iBAAiB,SAKhC"}
|
package/dist/store/use-scene.js
CHANGED
|
@@ -55,11 +55,10 @@ const useScene = create()(temporal((set, get) => ({
|
|
|
55
55
|
dirtyNodes: new Set(),
|
|
56
56
|
// 4. Collections
|
|
57
57
|
collections: {},
|
|
58
|
+
// 5. Read-only lock
|
|
59
|
+
readOnly: false,
|
|
60
|
+
setReadOnly: (readOnly) => set({ readOnly }),
|
|
58
61
|
unloadScene: () => {
|
|
59
|
-
// Clear temporal tracking to prevent memory leaks from stale node references
|
|
60
|
-
prevPastLength = 0;
|
|
61
|
-
prevFutureLength = 0;
|
|
62
|
-
prevNodesSnapshot = null;
|
|
63
62
|
set({
|
|
64
63
|
nodes: {},
|
|
65
64
|
rootNodeIds: [],
|
|
@@ -74,14 +73,22 @@ const useScene = create()(temporal((set, get) => ({
|
|
|
74
73
|
setScene: (nodes, rootNodeIds) => {
|
|
75
74
|
// Apply backward compatibility migrations
|
|
76
75
|
const patchedNodes = migrateNodes(nodes);
|
|
76
|
+
// Remove orphans: nodes whose parentId points to a non-existent node
|
|
77
|
+
const cleanedNodes = { ...patchedNodes };
|
|
78
|
+
for (const node of Object.values(cleanedNodes)) {
|
|
79
|
+
if (node.parentId && !cleanedNodes[node.parentId]) {
|
|
80
|
+
console.warn('[Scene] Removing orphan node', node.id, '(parentId', node.parentId, 'not found)');
|
|
81
|
+
delete cleanedNodes[node.id];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
77
84
|
set({
|
|
78
|
-
nodes:
|
|
85
|
+
nodes: cleanedNodes,
|
|
79
86
|
rootNodeIds,
|
|
80
87
|
dirtyNodes: new Set(),
|
|
81
88
|
collections: {},
|
|
82
89
|
});
|
|
83
90
|
// Mark all nodes as dirty to trigger re-validation
|
|
84
|
-
Object.values(
|
|
91
|
+
Object.values(cleanedNodes).forEach((node) => {
|
|
85
92
|
get().markDirty(node.id);
|
|
86
93
|
});
|
|
87
94
|
},
|
|
@@ -129,6 +136,8 @@ const useScene = create()(temporal((set, get) => ({
|
|
|
129
136
|
deleteNode: (id) => nodeActions.deleteNodesAction(set, get, [id]),
|
|
130
137
|
// --- COLLECTIONS ---
|
|
131
138
|
createCollection: (name, nodeIds = []) => {
|
|
139
|
+
if (get().readOnly)
|
|
140
|
+
return '';
|
|
132
141
|
const id = generateCollectionId();
|
|
133
142
|
const collection = { id, name, nodeIds };
|
|
134
143
|
set((state) => {
|
|
@@ -147,6 +156,8 @@ const useScene = create()(temporal((set, get) => ({
|
|
|
147
156
|
return id;
|
|
148
157
|
},
|
|
149
158
|
deleteCollection: (id) => {
|
|
159
|
+
if (get().readOnly)
|
|
160
|
+
return;
|
|
150
161
|
set((state) => {
|
|
151
162
|
const col = state.collections[id];
|
|
152
163
|
const nextCollections = { ...state.collections };
|
|
@@ -166,6 +177,8 @@ const useScene = create()(temporal((set, get) => ({
|
|
|
166
177
|
});
|
|
167
178
|
},
|
|
168
179
|
updateCollection: (id, data) => {
|
|
180
|
+
if (get().readOnly)
|
|
181
|
+
return;
|
|
169
182
|
set((state) => {
|
|
170
183
|
const col = state.collections[id];
|
|
171
184
|
if (!col)
|
|
@@ -174,6 +187,8 @@ const useScene = create()(temporal((set, get) => ({
|
|
|
174
187
|
});
|
|
175
188
|
},
|
|
176
189
|
addToCollection: (id, nodeId) => {
|
|
190
|
+
if (get().readOnly)
|
|
191
|
+
return;
|
|
177
192
|
set((state) => {
|
|
178
193
|
const col = state.collections[id];
|
|
179
194
|
if (!col || col.nodeIds.includes(nodeId))
|
|
@@ -194,6 +209,8 @@ const useScene = create()(temporal((set, get) => ({
|
|
|
194
209
|
});
|
|
195
210
|
},
|
|
196
211
|
removeFromCollection: (id, nodeId) => {
|
|
212
|
+
if (get().readOnly)
|
|
213
|
+
return;
|
|
197
214
|
set((state) => {
|
|
198
215
|
const col = state.collections[id];
|
|
199
216
|
if (!col)
|
|
@@ -227,19 +244,12 @@ export default useScene;
|
|
|
227
244
|
let prevPastLength = 0;
|
|
228
245
|
let prevFutureLength = 0;
|
|
229
246
|
let prevNodesSnapshot = null;
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
* Should be called when unloading a scene to release node references.
|
|
233
|
-
*/
|
|
234
|
-
export function clearTemporalTracking() {
|
|
247
|
+
export function clearSceneHistory() {
|
|
248
|
+
useScene.temporal.getState().clear();
|
|
235
249
|
prevPastLength = 0;
|
|
236
250
|
prevFutureLength = 0;
|
|
237
251
|
prevNodesSnapshot = null;
|
|
238
252
|
}
|
|
239
|
-
export function clearSceneHistory() {
|
|
240
|
-
useScene.temporal.getState().clear();
|
|
241
|
-
clearTemporalTracking();
|
|
242
|
-
}
|
|
243
253
|
// Subscribe to the temporal store (Undo/Redo events)
|
|
244
254
|
useScene.temporal.subscribe((state) => {
|
|
245
255
|
const currentPastLength = state.pastStates.length;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"door-system.d.ts","sourceRoot":"","sources":["../../../src/systems/door/door-system.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"door-system.d.ts","sourceRoot":"","sources":["../../../src/systems/door/door-system.tsx"],"names":[],"mappings":"AAUA,eAAO,MAAM,UAAU,YA2BtB,CAAA"}
|
|
@@ -1,24 +1,8 @@
|
|
|
1
1
|
import { useFrame } from '@react-three/fiber';
|
|
2
2
|
import * as THREE from 'three';
|
|
3
|
-
import { DoubleSide, MeshStandardNodeMaterial } from 'three/webgpu';
|
|
4
3
|
import { sceneRegistry } from '../../hooks/scene-registry/scene-registry';
|
|
4
|
+
import { baseMaterial, glassMaterial } from '../../materials';
|
|
5
5
|
import useScene from '../../store/use-scene';
|
|
6
|
-
const baseMaterial = new MeshStandardNodeMaterial({
|
|
7
|
-
name: 'door-base',
|
|
8
|
-
color: '#f2f0ed',
|
|
9
|
-
roughness: 0.5,
|
|
10
|
-
metalness: 0,
|
|
11
|
-
});
|
|
12
|
-
const glassMaterial = new MeshStandardNodeMaterial({
|
|
13
|
-
name: 'door-glass',
|
|
14
|
-
color: 'lightblue',
|
|
15
|
-
roughness: 0.05,
|
|
16
|
-
metalness: 0.1,
|
|
17
|
-
transparent: true,
|
|
18
|
-
opacity: 0.35,
|
|
19
|
-
side: DoubleSide,
|
|
20
|
-
depthWrite: false,
|
|
21
|
-
});
|
|
22
6
|
// Invisible material for root mesh — used as selection hitbox only
|
|
23
7
|
const hitboxMaterial = new THREE.MeshBasicMaterial({ visible: false });
|
|
24
8
|
export const DoorSystem = () => {
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
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;AA+BjF,eAAO,MAAM,UAAU,YAuFtB,CAAA;AAuLD;;;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,CAoDvF"}
|
|
@@ -7,7 +7,13 @@ import { sceneRegistry } from '../../hooks/scene-registry/scene-registry';
|
|
|
7
7
|
import useScene from '../../store/use-scene';
|
|
8
8
|
const csgEvaluator = new Evaluator();
|
|
9
9
|
csgEvaluator.useGroups = true;
|
|
10
|
+
csgEvaluator.consolidateGroups = false; // shared dummyMats across brushes causes consolidation to misalign groupIndices vs groupOrder indices → crash
|
|
10
11
|
csgEvaluator.attributes = ['position', 'normal'];
|
|
12
|
+
function prepareBrushForCSG(brush) {
|
|
13
|
+
brush.geometry.computeBoundsTree = computeBoundsTree;
|
|
14
|
+
brush.geometry.computeBoundsTree({ maxLeafSize: 10 });
|
|
15
|
+
brush.updateMatrixWorld();
|
|
16
|
+
}
|
|
11
17
|
// Pooled objects to avoid per-frame allocation in updateMergedRoofGeometry
|
|
12
18
|
const _matrix = new THREE.Matrix4();
|
|
13
19
|
const _position = new THREE.Vector3();
|
|
@@ -71,6 +77,9 @@ export const RoofSystem = () => {
|
|
|
71
77
|
}
|
|
72
78
|
clearDirty(id);
|
|
73
79
|
}
|
|
80
|
+
else {
|
|
81
|
+
clearDirty(id);
|
|
82
|
+
}
|
|
74
83
|
// Queue the parent roof for a merged geometry update
|
|
75
84
|
if (node.parentId) {
|
|
76
85
|
pendingRoofUpdates.add(node.parentId);
|
|
@@ -151,6 +160,7 @@ function updateMergedRoofGeometry(roofNode, group, nodes) {
|
|
|
151
160
|
const next = csgEvaluator.evaluate(totalShinSlab, brushes.shinSlab, ADDITION);
|
|
152
161
|
totalShinSlab.geometry.dispose();
|
|
153
162
|
brushes.shinSlab.geometry.dispose();
|
|
163
|
+
prepareBrushForCSG(next);
|
|
154
164
|
totalShinSlab = next;
|
|
155
165
|
}
|
|
156
166
|
else {
|
|
@@ -160,6 +170,7 @@ function updateMergedRoofGeometry(roofNode, group, nodes) {
|
|
|
160
170
|
const next = csgEvaluator.evaluate(totalDeckSlab, brushes.deckSlab, ADDITION);
|
|
161
171
|
totalDeckSlab.geometry.dispose();
|
|
162
172
|
brushes.deckSlab.geometry.dispose();
|
|
173
|
+
prepareBrushForCSG(next);
|
|
163
174
|
totalDeckSlab = next;
|
|
164
175
|
}
|
|
165
176
|
else {
|
|
@@ -169,6 +180,7 @@ function updateMergedRoofGeometry(roofNode, group, nodes) {
|
|
|
169
180
|
const next = csgEvaluator.evaluate(totalWall, brushes.wallBrush, ADDITION);
|
|
170
181
|
totalWall.geometry.dispose();
|
|
171
182
|
brushes.wallBrush.geometry.dispose();
|
|
183
|
+
prepareBrushForCSG(next);
|
|
172
184
|
totalWall = next;
|
|
173
185
|
}
|
|
174
186
|
else {
|
|
@@ -178,6 +190,7 @@ function updateMergedRoofGeometry(roofNode, group, nodes) {
|
|
|
178
190
|
const next = csgEvaluator.evaluate(totalInner, brushes.innerBrush, ADDITION);
|
|
179
191
|
totalInner.geometry.dispose();
|
|
180
192
|
brushes.innerBrush.geometry.dispose();
|
|
193
|
+
prepareBrushForCSG(next);
|
|
181
194
|
totalInner = next;
|
|
182
195
|
}
|
|
183
196
|
else {
|
|
@@ -380,6 +393,11 @@ export function getRoofSegmentBrushes(node) {
|
|
|
380
393
|
return null;
|
|
381
394
|
if (!geo.index)
|
|
382
395
|
return null;
|
|
396
|
+
// Strip zero-count groups — three-bvh-csg crashes with groupIndices[i] undefined
|
|
397
|
+
// when a group exists but covers no triangles (can happen after mergeVertices)
|
|
398
|
+
geo.groups = geo.groups.filter((g) => g.count > 0);
|
|
399
|
+
if (geo.groups.length === 0)
|
|
400
|
+
return null;
|
|
383
401
|
geo.computeBoundsTree = computeBoundsTree;
|
|
384
402
|
geo.computeBoundsTree({ maxLeafSize: 10 });
|
|
385
403
|
const brush = new Brush(geo, dummyMats);
|
|
@@ -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;
|
|
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;AAuED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,GAAG,KAAK,CAAC,cAAc,CAG7E"}
|