@pascal-app/core 0.1.11 → 0.1.13
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 +10 -2
- package/dist/events/bus.d.ts.map +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-manager.d.ts +9 -4
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-manager.js +58 -13
- package/dist/hooks/spatial-grid/use-spatial-query.d.ts +5 -0
- package/dist/hooks/spatial-grid/use-spatial-query.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/wall-spatial-grid.d.ts +4 -1
- package/dist/hooks/spatial-grid/wall-spatial-grid.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/wall-spatial-grid.js +33 -8
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -5
- package/dist/schema/index.d.ts +1 -0
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +1 -0
- package/dist/schema/nodes/ceiling.d.ts +1 -0
- package/dist/schema/nodes/ceiling.d.ts.map +1 -1
- package/dist/schema/nodes/ceiling.js +2 -0
- package/dist/schema/nodes/item.d.ts +9 -0
- package/dist/schema/nodes/item.d.ts.map +1 -1
- package/dist/schema/nodes/item.js +8 -0
- package/dist/schema/nodes/site.d.ts +5 -0
- package/dist/schema/nodes/site.d.ts.map +1 -1
- package/dist/schema/nodes/slab.d.ts +1 -0
- package/dist/schema/nodes/slab.d.ts.map +1 -1
- package/dist/schema/nodes/slab.js +1 -0
- package/dist/schema/nodes/window.d.ts +40 -0
- package/dist/schema/nodes/window.d.ts.map +1 -0
- package/dist/schema/nodes/window.js +38 -0
- package/dist/schema/types.d.ts +48 -0
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/types.js +2 -0
- package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
- package/dist/systems/ceiling/ceiling-system.js +15 -0
- package/dist/systems/item/item-system.d.ts.map +1 -1
- package/dist/systems/item/item-system.js +8 -4
- package/dist/systems/slab/slab-system.d.ts.map +1 -1
- package/dist/systems/slab/slab-system.js +15 -0
- package/dist/systems/wall/wall-system.d.ts.map +1 -1
- package/dist/systems/wall/wall-system.js +15 -9
- package/dist/systems/window/window-system.d.ts +2 -0
- package/dist/systems/window/window-system.d.ts.map +1 -0
- package/dist/systems/window/window-system.js +147 -0
- package/package.json +6 -3
package/dist/events/bus.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ThreeEvent } from '@react-three/fiber';
|
|
2
|
-
import type { BuildingNode, CeilingNode, ItemNode, LevelNode, RoofNode, SiteNode, SlabNode, WallNode, ZoneNode } from '../schema';
|
|
2
|
+
import type { BuildingNode, CeilingNode, ItemNode, LevelNode, RoofNode, SiteNode, SlabNode, WallNode, WindowNode, ZoneNode } from '../schema';
|
|
3
3
|
import type { AnyNode } from '../schema/types';
|
|
4
4
|
export interface GridEvent {
|
|
5
5
|
position: [number, number, number];
|
|
@@ -22,6 +22,7 @@ export type ZoneEvent = NodeEvent<ZoneNode>;
|
|
|
22
22
|
export type SlabEvent = NodeEvent<SlabNode>;
|
|
23
23
|
export type CeilingEvent = NodeEvent<CeilingNode>;
|
|
24
24
|
export type RoofEvent = NodeEvent<RoofNode>;
|
|
25
|
+
export type WindowEvent = NodeEvent<WindowNode>;
|
|
25
26
|
export declare const eventSuffixes: readonly ["click", "move", "enter", "leave", "pointerdown", "pointerup", "context-menu", "double-click"];
|
|
26
27
|
export type EventSuffix = (typeof eventSuffixes)[number];
|
|
27
28
|
type NodeEvents<T extends string, E> = {
|
|
@@ -33,14 +34,21 @@ type GridEvents = {
|
|
|
33
34
|
export interface CameraControlEvent {
|
|
34
35
|
nodeId: AnyNode['id'];
|
|
35
36
|
}
|
|
37
|
+
export interface ThumbnailGenerateEvent {
|
|
38
|
+
projectId: string;
|
|
39
|
+
}
|
|
36
40
|
type CameraControlEvents = {
|
|
37
41
|
'camera-controls:view': CameraControlEvent;
|
|
38
42
|
'camera-controls:capture': CameraControlEvent;
|
|
39
43
|
'camera-controls:top-view': undefined;
|
|
40
44
|
'camera-controls:orbit-cw': undefined;
|
|
41
45
|
'camera-controls:orbit-ccw': undefined;
|
|
46
|
+
'camera-controls:generate-thumbnail': ThumbnailGenerateEvent;
|
|
47
|
+
};
|
|
48
|
+
type ToolEvents = {
|
|
49
|
+
'tool:cancel': undefined;
|
|
42
50
|
};
|
|
43
|
-
type EditorEvents = GridEvents & NodeEvents<'wall', WallEvent> & NodeEvents<'item', ItemEvent> & NodeEvents<'site', SiteEvent> & NodeEvents<'building', BuildingEvent> & NodeEvents<'level', LevelEvent> & NodeEvents<'zone', ZoneEvent> & NodeEvents<'slab', SlabEvent> & NodeEvents<'ceiling', CeilingEvent> & NodeEvents<'roof', RoofEvent> & CameraControlEvents;
|
|
51
|
+
type EditorEvents = GridEvents & NodeEvents<'wall', WallEvent> & NodeEvents<'item', ItemEvent> & NodeEvents<'site', SiteEvent> & NodeEvents<'building', BuildingEvent> & NodeEvents<'level', LevelEvent> & NodeEvents<'zone', ZoneEvent> & NodeEvents<'slab', SlabEvent> & NodeEvents<'ceiling', CeilingEvent> & NodeEvents<'roof', RoofEvent> & NodeEvents<'window', WindowEvent> & CameraControlEvents & ToolEvents;
|
|
44
52
|
export declare const emitter: import("mitt").Emitter<EditorEvents>;
|
|
45
53
|
export {};
|
|
46
54
|
//# sourceMappingURL=bus.d.ts.map
|
package/dist/events/bus.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bus.d.ts","sourceRoot":"","sources":["../../src/events/bus.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAEpD,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"bus.d.ts","sourceRoot":"","sources":["../../src/events/bus.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,6 +13,7 @@ export declare const sceneRegistry: {
|
|
|
13
13
|
roof: Set<string>;
|
|
14
14
|
scan: Set<string>;
|
|
15
15
|
guide: Set<string>;
|
|
16
|
+
window: Set<string>;
|
|
16
17
|
};
|
|
17
18
|
};
|
|
18
19
|
export declare function useRegistry(id: string, type: keyof typeof sceneRegistry.byType, ref: React.RefObject<THREE.Object3D>): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scene-registry.d.ts","sourceRoot":"","sources":["../../../src/hooks/scene-registry/scene-registry.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AAEpC,eAAO,MAAM,aAAa
|
|
1
|
+
{"version":3,"file":"scene-registry.d.ts","sourceRoot":"","sources":["../../../src/hooks/scene-registry/scene-registry.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AAEpC,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;CAoBzB,CAAC;AAEF,wBAAgB,WAAW,CACzB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,OAAO,aAAa,CAAC,MAAM,EACvC,GAAG,EAAE,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,QAkBrC"}
|
|
@@ -56,28 +56,33 @@ export declare class SpatialGridManager {
|
|
|
56
56
|
canPlaceOnWall(levelId: string, wallId: string, localX: number, localY: number, dimensions: [number, number, number], attachType?: 'wall' | 'wall-side', side?: 'front' | 'back', ignoreIds?: string[]): {
|
|
57
57
|
valid: boolean;
|
|
58
58
|
conflictIds: string[];
|
|
59
|
+
adjustedY: number;
|
|
60
|
+
wasAdjusted: boolean;
|
|
61
|
+
} | {
|
|
62
|
+
valid: boolean;
|
|
63
|
+
conflictIds: never[];
|
|
59
64
|
};
|
|
60
65
|
getWallForItem(levelId: string, itemId: string): string | undefined;
|
|
61
66
|
/**
|
|
62
67
|
* Get the total slab elevation at a given (x, z) position on a level.
|
|
63
|
-
* Returns the highest slab elevation if the point is inside any slab polygon, otherwise 0.
|
|
68
|
+
* Returns the highest slab elevation if the point is inside any slab polygon (but not in any holes), otherwise 0.
|
|
64
69
|
*/
|
|
65
70
|
getSlabElevationAt(levelId: string, x: number, z: number): number;
|
|
66
71
|
/**
|
|
67
72
|
* Get the slab elevation for an item using its full footprint (bounding box).
|
|
68
|
-
* Checks if any part of the item's rotated footprint overlaps with any slab polygon.
|
|
73
|
+
* Checks if any part of the item's rotated footprint overlaps with any slab polygon (excluding holes).
|
|
69
74
|
* Returns the highest overlapping slab elevation, or 0 if none.
|
|
70
75
|
*/
|
|
71
76
|
getSlabElevationForItem(levelId: string, position: [number, number, number], dimensions: [number, number, number], rotation: [number, number, number]): number;
|
|
72
77
|
/**
|
|
73
|
-
* Get the slab elevation for a wall by checking if it overlaps with any slab polygon.
|
|
78
|
+
* Get the slab elevation for a wall by checking if it overlaps with any slab polygon (excluding holes).
|
|
74
79
|
* Uses wallOverlapsPolygon which handles edge cases (points on boundary, collinear segments).
|
|
75
80
|
* Returns the highest slab elevation found, or 0 if none.
|
|
76
81
|
*/
|
|
77
82
|
getSlabElevationForWall(levelId: string, start: [number, number], end: [number, number]): number;
|
|
78
83
|
/**
|
|
79
84
|
* Check if an item can be placed on a ceiling.
|
|
80
|
-
* Validates that the footprint is within the ceiling polygon and doesn't overlap other ceiling items.
|
|
85
|
+
* Validates that the footprint is within the ceiling polygon (but not in any holes) and doesn't overlap other ceiling items.
|
|
81
86
|
*/
|
|
82
87
|
canPlaceOnCeiling(ceilingId: string, position: [number, number, number], dimensions: [number, number, number], rotation: [number, number, number], ignoreIds?: string[]): {
|
|
83
88
|
valid: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spatial-grid-manager.d.ts","sourceRoot":"","sources":["../../../src/hooks/spatial-grid/spatial-grid-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAA6C,MAAM,cAAc,CAAA;AAQtF;;GAEG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,OAAO,CAYhG;AAgFD;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAClC,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EACpC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAClC,OAAO,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAChC,KAAK,SAAI,GACR,OAAO,CAwBT;AAiCD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EACvB,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EACrB,OAAO,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAC/B,OAAO,CAyBT;AAED,qBAAa,kBAAkB;IASjB,OAAO,CAAC,QAAQ;IAR5B,OAAO,CAAC,UAAU,CAAiC;IACnD,OAAO,CAAC,SAAS,CAAqC;IACtD,OAAO,CAAC,KAAK,CAA8B;IAC3C,OAAO,CAAC,YAAY,CAA2C;IAC/D,OAAO,CAAC,YAAY,CAAiC;IACrD,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,cAAc,CAA4B;gBAE9B,QAAQ,SAAM;IAElC,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,UAAU;IAQlB,iBAAiB,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM;IAoDhD,iBAAiB,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM;IA0DhD,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAyBnE,eAAe,CACb,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAClC,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EACpC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAClC,SAAS,CAAC,EAAE,MAAM,EAAE;;;;IAMtB;;;;;;;;;;OAUG;IACH,cAAc,CACZ,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EACpC,UAAU,GAAE,MAAM,GAAG,WAAoB,EACzC,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,EACvB,SAAS,CAAC,EAAE,MAAM,EAAE
|
|
1
|
+
{"version":3,"file":"spatial-grid-manager.d.ts","sourceRoot":"","sources":["../../../src/hooks/spatial-grid/spatial-grid-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -369,7 +369,7 @@ export class SpatialGridManager {
|
|
|
369
369
|
}
|
|
370
370
|
/**
|
|
371
371
|
* Get the total slab elevation at a given (x, z) position on a level.
|
|
372
|
-
* Returns the highest slab elevation if the point is inside any slab polygon, otherwise 0.
|
|
372
|
+
* Returns the highest slab elevation if the point is inside any slab polygon (but not in any holes), otherwise 0.
|
|
373
373
|
*/
|
|
374
374
|
getSlabElevationAt(levelId, x, z) {
|
|
375
375
|
const slabMap = this.slabsByLevel.get(levelId);
|
|
@@ -378,9 +378,20 @@ export class SpatialGridManager {
|
|
|
378
378
|
let maxElevation = 0;
|
|
379
379
|
for (const slab of slabMap.values()) {
|
|
380
380
|
if (slab.polygon.length >= 3 && pointInPolygon(x, z, slab.polygon)) {
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
381
|
+
// Check if point is in any hole
|
|
382
|
+
let inHole = false;
|
|
383
|
+
const holes = slab.holes || [];
|
|
384
|
+
for (const hole of holes) {
|
|
385
|
+
if (hole.length >= 3 && pointInPolygon(x, z, hole)) {
|
|
386
|
+
inHole = true;
|
|
387
|
+
break;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
if (!inHole) {
|
|
391
|
+
const elevation = slab.elevation ?? 0.05;
|
|
392
|
+
if (elevation > maxElevation) {
|
|
393
|
+
maxElevation = elevation;
|
|
394
|
+
}
|
|
384
395
|
}
|
|
385
396
|
}
|
|
386
397
|
}
|
|
@@ -388,7 +399,7 @@ export class SpatialGridManager {
|
|
|
388
399
|
}
|
|
389
400
|
/**
|
|
390
401
|
* Get the slab elevation for an item using its full footprint (bounding box).
|
|
391
|
-
* Checks if any part of the item's rotated footprint overlaps with any slab polygon.
|
|
402
|
+
* Checks if any part of the item's rotated footprint overlaps with any slab polygon (excluding holes).
|
|
392
403
|
* Returns the highest overlapping slab elevation, or 0 if none.
|
|
393
404
|
*/
|
|
394
405
|
getSlabElevationForItem(levelId, position, dimensions, rotation) {
|
|
@@ -398,16 +409,29 @@ export class SpatialGridManager {
|
|
|
398
409
|
let maxElevation = -Infinity;
|
|
399
410
|
for (const slab of slabMap.values()) {
|
|
400
411
|
if (slab.polygon.length >= 3 && itemOverlapsPolygon(position, dimensions, rotation, slab.polygon, 0.01)) {
|
|
401
|
-
|
|
402
|
-
if
|
|
403
|
-
|
|
412
|
+
// Check if item is entirely within a hole (if so, ignore this slab)
|
|
413
|
+
// We consider it entirely in a hole if the item center is in the hole
|
|
414
|
+
let inHole = false;
|
|
415
|
+
const [cx, , cz] = position;
|
|
416
|
+
const holes = slab.holes || [];
|
|
417
|
+
for (const hole of holes) {
|
|
418
|
+
if (hole.length >= 3 && pointInPolygon(cx, cz, hole)) {
|
|
419
|
+
inHole = true;
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
if (!inHole) {
|
|
424
|
+
const elevation = slab.elevation ?? 0.05;
|
|
425
|
+
if (elevation > maxElevation) {
|
|
426
|
+
maxElevation = elevation;
|
|
427
|
+
}
|
|
404
428
|
}
|
|
405
429
|
}
|
|
406
430
|
}
|
|
407
431
|
return maxElevation === -Infinity ? 0 : maxElevation;
|
|
408
432
|
}
|
|
409
433
|
/**
|
|
410
|
-
* Get the slab elevation for a wall by checking if it overlaps with any slab polygon.
|
|
434
|
+
* Get the slab elevation for a wall by checking if it overlaps with any slab polygon (excluding holes).
|
|
411
435
|
* Uses wallOverlapsPolygon which handles edge cases (points on boundary, collinear segments).
|
|
412
436
|
* Returns the highest slab elevation found, or 0 if none.
|
|
413
437
|
*/
|
|
@@ -420,9 +444,22 @@ export class SpatialGridManager {
|
|
|
420
444
|
if (slab.polygon.length < 3)
|
|
421
445
|
continue;
|
|
422
446
|
if (wallOverlapsPolygon(start, end, slab.polygon)) {
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
447
|
+
// Check if wall midpoint is in a hole (if so, ignore this slab)
|
|
448
|
+
let inHole = false;
|
|
449
|
+
const midX = (start[0] + end[0]) / 2;
|
|
450
|
+
const midZ = (start[1] + end[1]) / 2;
|
|
451
|
+
const holes = slab.holes || [];
|
|
452
|
+
for (const hole of holes) {
|
|
453
|
+
if (hole.length >= 3 && pointInPolygon(midX, midZ, hole)) {
|
|
454
|
+
inHole = true;
|
|
455
|
+
break;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
if (!inHole) {
|
|
459
|
+
const elevation = slab.elevation ?? 0.05;
|
|
460
|
+
if (elevation > maxElevation) {
|
|
461
|
+
maxElevation = elevation;
|
|
462
|
+
}
|
|
426
463
|
}
|
|
427
464
|
}
|
|
428
465
|
}
|
|
@@ -430,7 +467,7 @@ export class SpatialGridManager {
|
|
|
430
467
|
}
|
|
431
468
|
/**
|
|
432
469
|
* Check if an item can be placed on a ceiling.
|
|
433
|
-
* Validates that the footprint is within the ceiling polygon and doesn't overlap other ceiling items.
|
|
470
|
+
* Validates that the footprint is within the ceiling polygon (but not in any holes) and doesn't overlap other ceiling items.
|
|
434
471
|
*/
|
|
435
472
|
canPlaceOnCeiling(ceilingId, position, dimensions, rotation, ignoreIds) {
|
|
436
473
|
const ceiling = this.ceilings.get(ceilingId);
|
|
@@ -444,6 +481,14 @@ export class SpatialGridManager {
|
|
|
444
481
|
return { valid: false, conflictIds: [] };
|
|
445
482
|
}
|
|
446
483
|
}
|
|
484
|
+
// Check if item center is in any hole (if so, it cannot be placed)
|
|
485
|
+
const [centerX, , centerZ] = position;
|
|
486
|
+
const holes = ceiling.holes || [];
|
|
487
|
+
for (const hole of holes) {
|
|
488
|
+
if (hole.length >= 3 && pointInPolygon(centerX, centerZ, hole)) {
|
|
489
|
+
return { valid: false, conflictIds: [] };
|
|
490
|
+
}
|
|
491
|
+
}
|
|
447
492
|
// Check for overlaps with other ceiling items
|
|
448
493
|
return this.getCeilingGrid(ceilingId).canPlace(position, dimensions, rotation, ignoreIds);
|
|
449
494
|
}
|
|
@@ -7,6 +7,11 @@ export declare function useSpatialQuery(): {
|
|
|
7
7
|
canPlaceOnWall: (levelId: LevelNode["id"], wallId: WallNode["id"], localX: number, localY: number, dimensions: [number, number, number], attachType?: "wall" | "wall-side", side?: "front" | "back", ignoreIds?: string[]) => {
|
|
8
8
|
valid: boolean;
|
|
9
9
|
conflictIds: string[];
|
|
10
|
+
adjustedY: number;
|
|
11
|
+
wasAdjusted: boolean;
|
|
12
|
+
} | {
|
|
13
|
+
valid: boolean;
|
|
14
|
+
conflictIds: never[];
|
|
10
15
|
};
|
|
11
16
|
canPlaceOnCeiling: (ceilingId: CeilingNode["id"], position: [number, number, number], dimensions: [number, number, number], rotation: [number, number, number], ignoreIds?: string[]) => {
|
|
12
17
|
valid: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-spatial-query.d.ts","sourceRoot":"","sources":["../../../src/hooks/spatial-grid/use-spatial-query.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAGpE,wBAAgB,eAAe;+BAGhB,SAAS,CAAC,IAAI,CAAC,YACd,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,cACtB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,YAC1B,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,cACtB,MAAM,EAAE;;;;8BASX,SAAS,CAAC,IAAI,CAAC,UAChB,QAAQ,CAAC,IAAI,CAAC,UACd,MAAM,UACN,MAAM,cACF,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,eACxB,MAAM,GAAG,WAAW,SACzB,OAAO,GAAG,MAAM,cACX,MAAM,EAAE
|
|
1
|
+
{"version":3,"file":"use-spatial-query.d.ts","sourceRoot":"","sources":["../../../src/hooks/spatial-grid/use-spatial-query.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAGpE,wBAAgB,eAAe;+BAGhB,SAAS,CAAC,IAAI,CAAC,YACd,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,cACtB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,YAC1B,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,cACtB,MAAM,EAAE;;;;8BASX,SAAS,CAAC,IAAI,CAAC,UAChB,QAAQ,CAAC,IAAI,CAAC,UACd,MAAM,UACN,MAAM,cACF,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,eACxB,MAAM,GAAG,WAAW,SACzB,OAAO,GAAG,MAAM,cACX,MAAM,EAAE;;;;;;;;;mCAkBT,WAAW,CAAC,IAAI,CAAC,YAClB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,cACtB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,YAC1B,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,cACtB,MAAM,EAAE;;;;EAQzB"}
|
|
@@ -14,7 +14,7 @@ export declare class WallSpatialGrid {
|
|
|
14
14
|
private wallItems;
|
|
15
15
|
private itemToWall;
|
|
16
16
|
/**
|
|
17
|
-
* Check if an item can be placed on a wall
|
|
17
|
+
* Check if an item can be placed on a wall with auto-adjustment for vertical position
|
|
18
18
|
* @param wallId - The wall to place on
|
|
19
19
|
* @param wallLength - Length of the wall
|
|
20
20
|
* @param wallHeight - Height of the wall
|
|
@@ -25,10 +25,13 @@ export declare class WallSpatialGrid {
|
|
|
25
25
|
* @param attachType - 'wall' (blocks both sides) or 'wall-side' (blocks one side)
|
|
26
26
|
* @param side - Which side for 'wall-side' items
|
|
27
27
|
* @param ignoreIds - Item IDs to ignore in conflict check
|
|
28
|
+
* @returns Validation result with auto-adjusted Y position if needed
|
|
28
29
|
*/
|
|
29
30
|
canPlaceOnWall(wallId: string, wallLength: number, wallHeight: number, tCenter: number, itemWidth: number, yBottom: number, itemHeight: number, attachType?: AttachType, side?: WallSide, ignoreIds?: string[]): {
|
|
30
31
|
valid: boolean;
|
|
31
32
|
conflictIds: string[];
|
|
33
|
+
adjustedY: number;
|
|
34
|
+
wasAdjusted: boolean;
|
|
32
35
|
};
|
|
33
36
|
/**
|
|
34
37
|
* Check if two items conflict based on their attach types and sides
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wall-spatial-grid.d.ts","sourceRoot":"","sources":["../../../src/hooks/spatial-grid/wall-spatial-grid.ts"],"names":[],"mappings":"AAAA,KAAK,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAA;AAChC,KAAK,UAAU,GAAG,MAAM,GAAG,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"wall-spatial-grid.d.ts","sourceRoot":"","sources":["../../../src/hooks/spatial-grid/wall-spatial-grid.ts"],"names":[],"mappings":"AAAA,KAAK,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAA;AAChC,KAAK,UAAU,GAAG,MAAM,GAAG,WAAW,CAAA;AAQtC,UAAU,iBAAiB;IACzB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,IAAI,CAAC,EAAE,QAAQ,CAAA;CAChB;AAgCD,qBAAa,eAAe;IAC1B,OAAO,CAAC,SAAS,CAAyC;IAC1D,OAAO,CAAC,UAAU,CAA4B;IAE9C;;;;;;;;;;;;;OAaG;IACH,cAAc,CACZ,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,UAAU,GAAE,UAAmB,EAC/B,IAAI,CAAC,EAAE,QAAQ,EACf,SAAS,GAAE,MAAM,EAAO,GACvB;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,MAAM,EAAE,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,OAAO,CAAA;KAAE;IAsCrF;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IA0BzB,MAAM,CAAC,SAAS,EAAE,iBAAiB;IAUnC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IASrC,cAAc,CAAC,MAAM,EAAE,MAAM;IAQ7B,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE;IAapC,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIlD,KAAK;CAIN"}
|
|
@@ -1,10 +1,33 @@
|
|
|
1
1
|
// Small tolerance for floating point comparison to allow adjacent items
|
|
2
2
|
const EPSILON = 0.001;
|
|
3
|
+
// Margin from ceiling/floor when auto-snapping items
|
|
4
|
+
const AUTO_SNAP_MARGIN = 0.05;
|
|
5
|
+
/**
|
|
6
|
+
* Auto-adjust Y position to fit item within wall bounds
|
|
7
|
+
* Returns the adjusted Y position (bottom of item)
|
|
8
|
+
*/
|
|
9
|
+
function autoAdjustYPosition(yBottom, itemHeight, wallHeight) {
|
|
10
|
+
const yTop = yBottom + itemHeight;
|
|
11
|
+
// If fits perfectly, no adjustment needed
|
|
12
|
+
if (yBottom >= 0 && yTop <= wallHeight) {
|
|
13
|
+
return { adjustedY: yBottom, wasAdjusted: false };
|
|
14
|
+
}
|
|
15
|
+
// If too high (top exceeds wall height), snap down from ceiling
|
|
16
|
+
if (yTop > wallHeight) {
|
|
17
|
+
const adjustedY = wallHeight - itemHeight - AUTO_SNAP_MARGIN;
|
|
18
|
+
return { adjustedY: Math.max(0, adjustedY), wasAdjusted: true };
|
|
19
|
+
}
|
|
20
|
+
// If too low (bottom below floor), snap up from floor
|
|
21
|
+
if (yBottom < 0) {
|
|
22
|
+
return { adjustedY: AUTO_SNAP_MARGIN, wasAdjusted: true };
|
|
23
|
+
}
|
|
24
|
+
return { adjustedY: yBottom, wasAdjusted: false };
|
|
25
|
+
}
|
|
3
26
|
export class WallSpatialGrid {
|
|
4
27
|
wallItems = new Map(); // wallId -> placements
|
|
5
28
|
itemToWall = new Map(); // itemId -> wallId (reverse lookup)
|
|
6
29
|
/**
|
|
7
|
-
* Check if an item can be placed on a wall
|
|
30
|
+
* Check if an item can be placed on a wall with auto-adjustment for vertical position
|
|
8
31
|
* @param wallId - The wall to place on
|
|
9
32
|
* @param wallLength - Length of the wall
|
|
10
33
|
* @param wallHeight - Height of the wall
|
|
@@ -15,18 +38,20 @@ export class WallSpatialGrid {
|
|
|
15
38
|
* @param attachType - 'wall' (blocks both sides) or 'wall-side' (blocks one side)
|
|
16
39
|
* @param side - Which side for 'wall-side' items
|
|
17
40
|
* @param ignoreIds - Item IDs to ignore in conflict check
|
|
41
|
+
* @returns Validation result with auto-adjusted Y position if needed
|
|
18
42
|
*/
|
|
19
43
|
canPlaceOnWall(wallId, wallLength, wallHeight, tCenter, itemWidth, yBottom, itemHeight, attachType = 'wall', side, ignoreIds = []) {
|
|
20
44
|
const halfW = itemWidth / wallLength / 2;
|
|
21
45
|
const tStart = tCenter - halfW;
|
|
22
46
|
const tEnd = tCenter + halfW;
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
// Check wall boundaries
|
|
27
|
-
if (tStart < 0 || tEnd > 1 || yStart < 0 || yEnd > wallHeight) {
|
|
28
|
-
return { valid: false, conflictIds: [] };
|
|
47
|
+
// Check horizontal boundaries (still reject if item exceeds wall width)
|
|
48
|
+
if (tStart < 0 || tEnd > 1) {
|
|
49
|
+
return { valid: false, conflictIds: [], adjustedY: yBottom, wasAdjusted: false };
|
|
29
50
|
}
|
|
51
|
+
// Auto-adjust vertical position to fit within wall bounds
|
|
52
|
+
const { adjustedY, wasAdjusted } = autoAdjustYPosition(yBottom, itemHeight, wallHeight);
|
|
53
|
+
const yStart = adjustedY;
|
|
54
|
+
const yEnd = adjustedY + itemHeight;
|
|
30
55
|
const existing = this.wallItems.get(wallId) ?? [];
|
|
31
56
|
const ignoreSet = new Set(ignoreIds);
|
|
32
57
|
const conflicts = [];
|
|
@@ -44,7 +69,7 @@ export class WallSpatialGrid {
|
|
|
44
69
|
}
|
|
45
70
|
}
|
|
46
71
|
}
|
|
47
|
-
return { valid: conflicts.length === 0, conflictIds: conflicts };
|
|
72
|
+
return { valid: conflicts.length === 0, conflictIds: conflicts, adjustedY, wasAdjusted };
|
|
48
73
|
}
|
|
49
74
|
/**
|
|
50
75
|
* Check if two items conflict based on their attach types and sides
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
export type { BuildingEvent, CameraControlEvent, EventSuffix, GridEvent, ItemEvent, LevelEvent, NodeEvent, SiteEvent, SlabEvent, WallEvent,
|
|
1
|
+
export type { BuildingEvent, CameraControlEvent, CeilingEvent, EventSuffix, GridEvent, ItemEvent, LevelEvent, NodeEvent, RoofEvent, SiteEvent, SlabEvent, WallEvent, WindowEvent, ZoneEvent, } from './events/bus';
|
|
2
2
|
export { emitter, eventSuffixes } from './events/bus';
|
|
3
3
|
export { sceneRegistry, useRegistry, } from './hooks/scene-registry/scene-registry';
|
|
4
|
+
export { pointInPolygon, spatialGridManager } from './hooks/spatial-grid/spatial-grid-manager';
|
|
4
5
|
export { initSpatialGridSync, resolveLevelId, } from './hooks/spatial-grid/spatial-grid-sync';
|
|
5
6
|
export { useSpatialQuery } from './hooks/spatial-grid/use-spatial-query';
|
|
6
|
-
export {
|
|
7
|
+
export { loadAssetUrl, saveAsset } from './lib/asset-storage';
|
|
8
|
+
export { detectSpacesForLevel, initSpaceDetectionSync, type Space, wallTouchesOthers, } from './lib/space-detection';
|
|
7
9
|
export * from './schema';
|
|
8
10
|
export { default as useScene } from './store/use-scene';
|
|
9
11
|
export { CeilingSystem } from './systems/ceiling/ceiling-system';
|
|
@@ -11,7 +13,6 @@ export { ItemSystem } from './systems/item/item-system';
|
|
|
11
13
|
export { RoofSystem } from './systems/roof/roof-system';
|
|
12
14
|
export { SlabSystem } from './systems/slab/slab-system';
|
|
13
15
|
export { WallSystem } from './systems/wall/wall-system';
|
|
16
|
+
export { WindowSystem } from './systems/window/window-system';
|
|
14
17
|
export { isObject } from './utils/types';
|
|
15
|
-
export { saveAsset, loadAssetUrl } from './lib/asset-storage';
|
|
16
|
-
export { detectSpacesForLevel, wallTouchesOthers, initSpaceDetectionSync, type Space } from './lib/space-detection';
|
|
17
18
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,YAAY,EACV,aAAa,EACb,kBAAkB,EAClB,WAAW,EACX,SAAS,EACT,SAAS,EACT,UAAU,EACV,SAAS,EACT,SAAS,EACT,SAAS,EACT,SAAS,EACT,SAAS,EACT,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,YAAY,EACV,aAAa,EACb,kBAAkB,EAClB,YAAY,EACZ,WAAW,EACX,SAAS,EACT,SAAS,EACT,UAAU,EACV,SAAS,EACT,SAAS,EACT,SAAS,EACT,SAAS,EACT,SAAS,EACT,WAAW,EACX,SAAS,GACV,MAAM,cAAc,CAAA;AAErB,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAErD,OAAO,EACL,aAAa,EACb,WAAW,GACZ,MAAM,uCAAuC,CAAA;AAC9C,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,2CAA2C,CAAA;AAC9F,OAAO,EACL,mBAAmB,EACnB,cAAc,GACf,MAAM,wCAAwC,CAAA;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,wCAAwC,CAAA;AAExE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAE7D,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,KAAK,KAAK,EACV,iBAAiB,GAClB,MAAM,uBAAuB,CAAA;AAE9B,cAAc,UAAU,CAAA;AACxB,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAEvD,OAAO,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAA;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAA;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAA;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAA;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAA;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAA;AAC7D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -3,9 +3,13 @@
|
|
|
3
3
|
export { emitter, eventSuffixes } from './events/bus';
|
|
4
4
|
// Hooks
|
|
5
5
|
export { sceneRegistry, useRegistry, } from './hooks/scene-registry/scene-registry';
|
|
6
|
+
export { pointInPolygon, spatialGridManager } from './hooks/spatial-grid/spatial-grid-manager';
|
|
6
7
|
export { initSpatialGridSync, resolveLevelId, } from './hooks/spatial-grid/spatial-grid-sync';
|
|
7
8
|
export { useSpatialQuery } from './hooks/spatial-grid/use-spatial-query';
|
|
8
|
-
|
|
9
|
+
// Asset storage
|
|
10
|
+
export { loadAssetUrl, saveAsset } from './lib/asset-storage';
|
|
11
|
+
// Space detection
|
|
12
|
+
export { detectSpacesForLevel, initSpaceDetectionSync, wallTouchesOthers, } from './lib/space-detection';
|
|
9
13
|
// Schema
|
|
10
14
|
export * from './schema';
|
|
11
15
|
export { default as useScene } from './store/use-scene';
|
|
@@ -15,8 +19,5 @@ export { ItemSystem } from './systems/item/item-system';
|
|
|
15
19
|
export { RoofSystem } from './systems/roof/roof-system';
|
|
16
20
|
export { SlabSystem } from './systems/slab/slab-system';
|
|
17
21
|
export { WallSystem } from './systems/wall/wall-system';
|
|
22
|
+
export { WindowSystem } from './systems/window/window-system';
|
|
18
23
|
export { isObject } from './utils/types';
|
|
19
|
-
// Asset storage
|
|
20
|
-
export { saveAsset, loadAssetUrl } from './lib/asset-storage';
|
|
21
|
-
// Space detection
|
|
22
|
-
export { detectSpacesForLevel, wallTouchesOthers, initSpaceDetectionSync } from './lib/space-detection';
|
package/dist/schema/index.d.ts
CHANGED
|
@@ -13,5 +13,6 @@ export { RoofNode } from './nodes/roof';
|
|
|
13
13
|
export { ScanNode } from './nodes/scan';
|
|
14
14
|
export { GuideNode } from './nodes/guide';
|
|
15
15
|
export type { AnyNodeId, AnyNodeType } from './types';
|
|
16
|
+
export { WindowNode } from './nodes/window';
|
|
16
17
|
export { AnyNode } from './types';
|
|
17
18
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schema/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAE3E,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACvC,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAEzC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAE7C,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AACzC,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schema/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAE3E,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACvC,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAEzC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAE7C,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AACzC,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAE3C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA"}
|
package/dist/schema/index.js
CHANGED
|
@@ -14,5 +14,6 @@ export { ZoneNode } from './nodes/zone';
|
|
|
14
14
|
export { RoofNode } from './nodes/roof';
|
|
15
15
|
export { ScanNode } from './nodes/scan';
|
|
16
16
|
export { GuideNode } from './nodes/guide';
|
|
17
|
+
export { WindowNode } from './nodes/window';
|
|
17
18
|
// Union types
|
|
18
19
|
export { AnyNode } from './types';
|
|
@@ -19,6 +19,7 @@ export declare const CeilingNode: z.ZodObject<{
|
|
|
19
19
|
type: z.ZodDefault<z.ZodLiteral<"ceiling">>;
|
|
20
20
|
children: z.ZodDefault<z.ZodArray<z.ZodDefault<z.ZodTemplateLiteral<`item_${string}`>>>>;
|
|
21
21
|
polygon: z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>>;
|
|
22
|
+
holes: z.ZodDefault<z.ZodArray<z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>>>>;
|
|
22
23
|
height: z.ZodDefault<z.ZodNumber>;
|
|
23
24
|
}, z.core.$strip>;
|
|
24
25
|
export type CeilingNode = z.infer<typeof CeilingNode>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ceiling.d.ts","sourceRoot":"","sources":["../../../src/schema/nodes/ceiling.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAIvB,eAAO,MAAM,WAAW
|
|
1
|
+
{"version":3,"file":"ceiling.d.ts","sourceRoot":"","sources":["../../../src/schema/nodes/ceiling.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAIvB,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;iBAevB,CAAA;AAED,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAA"}
|
|
@@ -9,8 +9,10 @@ export const CeilingNode = BaseNode.extend({
|
|
|
9
9
|
// Specific props
|
|
10
10
|
// Polygon boundary - array of [x, z] coordinates defining the ceiling
|
|
11
11
|
polygon: z.array(z.tuple([z.number(), z.number()])),
|
|
12
|
+
holes: z.array(z.array(z.tuple([z.number(), z.number()]))).default([]),
|
|
12
13
|
height: z.number().default(2.5), // Height in meters
|
|
13
14
|
}).describe(dedent `
|
|
14
15
|
Ceiling node - used to represent a ceiling in the building
|
|
15
16
|
- polygon: array of [x, z] points defining the ceiling boundary
|
|
17
|
+
- holes: array of polygons representing holes in the ceiling
|
|
16
18
|
`);
|
|
@@ -11,9 +11,13 @@ declare const assetSchema: z.ZodObject<{
|
|
|
11
11
|
"wall-side": "wall-side";
|
|
12
12
|
ceiling: "ceiling";
|
|
13
13
|
}>>;
|
|
14
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
14
15
|
offset: z.ZodDefault<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
|
|
15
16
|
rotation: z.ZodDefault<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
|
|
16
17
|
scale: z.ZodDefault<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
|
|
18
|
+
surface: z.ZodOptional<z.ZodObject<{
|
|
19
|
+
height: z.ZodNumber;
|
|
20
|
+
}, z.core.$strip>>;
|
|
17
21
|
}, z.core.$strip>;
|
|
18
22
|
export type AssetInput = z.input<typeof assetSchema>;
|
|
19
23
|
export type Asset = z.infer<typeof assetSchema>;
|
|
@@ -41,6 +45,7 @@ export declare const ItemNode: z.ZodObject<{
|
|
|
41
45
|
front: "front";
|
|
42
46
|
back: "back";
|
|
43
47
|
}>>;
|
|
48
|
+
children: z.ZodDefault<z.ZodArray<z.ZodDefault<z.ZodTemplateLiteral<`item_${string}`>>>>;
|
|
44
49
|
wallId: z.ZodOptional<z.ZodString>;
|
|
45
50
|
wallT: z.ZodOptional<z.ZodNumber>;
|
|
46
51
|
asset: z.ZodObject<{
|
|
@@ -55,9 +60,13 @@ export declare const ItemNode: z.ZodObject<{
|
|
|
55
60
|
"wall-side": "wall-side";
|
|
56
61
|
ceiling: "ceiling";
|
|
57
62
|
}>>;
|
|
63
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
58
64
|
offset: z.ZodDefault<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
|
|
59
65
|
rotation: z.ZodDefault<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
|
|
60
66
|
scale: z.ZodDefault<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
|
|
67
|
+
surface: z.ZodOptional<z.ZodObject<{
|
|
68
|
+
height: z.ZodNumber;
|
|
69
|
+
}, z.core.$strip>>;
|
|
61
70
|
}, z.core.$strip>;
|
|
62
71
|
}, z.core.$strip>;
|
|
63
72
|
export type ItemNode = z.infer<typeof ItemNode>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"item.d.ts","sourceRoot":"","sources":["../../../src/schema/nodes/item.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,QAAA,MAAM,WAAW
|
|
1
|
+
{"version":3,"file":"item.d.ts","sourceRoot":"","sources":["../../../src/schema/nodes/item.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,QAAA,MAAM,WAAW;;;;;;;;;;;;;;;;;;;iBAkBf,CAAA;AAEF,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAA;AACpD,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAA;AAE/C,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAyBnB,CAAA;AAEF,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,QAAQ,CAAC,CAAA"}
|
|
@@ -9,10 +9,16 @@ const assetSchema = z.object({
|
|
|
9
9
|
src: z.string(),
|
|
10
10
|
dimensions: z.tuple([z.number(), z.number(), z.number()]).default([1, 1, 1]), // [w, h, d]
|
|
11
11
|
attachTo: z.enum(['wall', 'wall-side', 'ceiling']).optional(),
|
|
12
|
+
tags: z.array(z.string()).optional(),
|
|
12
13
|
// These are "Corrective" transforms to normalize the GLB
|
|
13
14
|
offset: z.tuple([z.number(), z.number(), z.number()]).default([0, 0, 0]),
|
|
14
15
|
rotation: z.tuple([z.number(), z.number(), z.number()]).default([0, 0, 0]),
|
|
15
16
|
scale: z.tuple([z.number(), z.number(), z.number()]).default([1, 1, 1]),
|
|
17
|
+
surface: z
|
|
18
|
+
.object({
|
|
19
|
+
height: z.number(), // where things rest
|
|
20
|
+
})
|
|
21
|
+
.optional(), // undefined = can't place things on it
|
|
16
22
|
});
|
|
17
23
|
export const ItemNode = BaseNode.extend({
|
|
18
24
|
id: objectId('item'),
|
|
@@ -20,6 +26,7 @@ export const ItemNode = BaseNode.extend({
|
|
|
20
26
|
position: z.tuple([z.number(), z.number(), z.number()]).default([0, 0, 0]),
|
|
21
27
|
rotation: z.tuple([z.number(), z.number(), z.number()]).default([0, 0, 0]),
|
|
22
28
|
side: z.enum(['front', 'back']).optional(),
|
|
29
|
+
children: z.array(objectId('item')).default([]),
|
|
23
30
|
// Wall attachment properties (only used when asset.attachTo is "wall" or "wall-side")
|
|
24
31
|
wallId: z.string().optional(),
|
|
25
32
|
wallT: z.number().optional(), // 0-1 parametric position along wall
|
|
@@ -35,4 +42,5 @@ export const ItemNode = BaseNode.extend({
|
|
|
35
42
|
- offset: corrective position offset for the model
|
|
36
43
|
- rotation: corrective rotation for the model
|
|
37
44
|
- scale: corrective scale for the model
|
|
45
|
+
- tags: tags associated with the item
|
|
38
46
|
`);
|
|
@@ -66,6 +66,7 @@ export declare const SiteNode: z.ZodObject<{
|
|
|
66
66
|
front: "front";
|
|
67
67
|
back: "back";
|
|
68
68
|
}>>;
|
|
69
|
+
children: z.ZodDefault<z.ZodArray<z.ZodDefault<z.ZodTemplateLiteral<`item_${string}`>>>>;
|
|
69
70
|
wallId: z.ZodOptional<z.ZodString>;
|
|
70
71
|
wallT: z.ZodOptional<z.ZodNumber>;
|
|
71
72
|
asset: z.ZodObject<{
|
|
@@ -80,9 +81,13 @@ export declare const SiteNode: z.ZodObject<{
|
|
|
80
81
|
"wall-side": "wall-side";
|
|
81
82
|
ceiling: "ceiling";
|
|
82
83
|
}>>;
|
|
84
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
83
85
|
offset: z.ZodDefault<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
|
|
84
86
|
rotation: z.ZodDefault<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
|
|
85
87
|
scale: z.ZodDefault<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
|
|
88
|
+
surface: z.ZodOptional<z.ZodObject<{
|
|
89
|
+
height: z.ZodNumber;
|
|
90
|
+
}, z.core.$strip>>;
|
|
86
91
|
}, z.core.$strip>;
|
|
87
92
|
}, z.core.$strip>], "type">>>;
|
|
88
93
|
}, z.core.$strip>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"site.d.ts","sourceRoot":"","sources":["../../../src/schema/nodes/site.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAiBvB,eAAO,MAAM,QAAQ
|
|
1
|
+
{"version":3,"file":"site.d.ts","sourceRoot":"","sources":["../../../src/schema/nodes/site.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAiBvB,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwBpB,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,QAAQ,CAAC,CAAA"}
|
|
@@ -18,6 +18,7 @@ export declare const SlabNode: z.ZodObject<{
|
|
|
18
18
|
id: z.ZodDefault<z.ZodTemplateLiteral<`slab_${string}`>>;
|
|
19
19
|
type: z.ZodDefault<z.ZodLiteral<"slab">>;
|
|
20
20
|
polygon: z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>>;
|
|
21
|
+
holes: z.ZodDefault<z.ZodArray<z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>>>>;
|
|
21
22
|
elevation: z.ZodDefault<z.ZodNumber>;
|
|
22
23
|
}, z.core.$strip>;
|
|
23
24
|
export type SlabNode = z.infer<typeof SlabNode>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slab.d.ts","sourceRoot":"","sources":["../../../src/schema/nodes/slab.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,eAAO,MAAM,QAAQ
|
|
1
|
+
{"version":3,"file":"slab.d.ts","sourceRoot":"","sources":["../../../src/schema/nodes/slab.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;iBAcpB,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,QAAQ,CAAC,CAAA"}
|
|
@@ -7,6 +7,7 @@ export const SlabNode = BaseNode.extend({
|
|
|
7
7
|
// Specific props
|
|
8
8
|
// Polygon boundary - array of [x, z] coordinates defining the slab
|
|
9
9
|
polygon: z.array(z.tuple([z.number(), z.number()])),
|
|
10
|
+
holes: z.array(z.array(z.tuple([z.number(), z.number()]))).default([]),
|
|
10
11
|
elevation: z.number().default(0.05), // Elevation in meters
|
|
11
12
|
}).describe(dedent `
|
|
12
13
|
Slab node - used to represent a slab/floor in the building
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const WindowNode: z.ZodObject<{
|
|
3
|
+
object: z.ZodDefault<z.ZodLiteral<"node">>;
|
|
4
|
+
name: z.ZodOptional<z.ZodString>;
|
|
5
|
+
parentId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
6
|
+
visible: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
7
|
+
camera: z.ZodOptional<z.ZodObject<{
|
|
8
|
+
position: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
|
|
9
|
+
target: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
|
|
10
|
+
mode: z.ZodDefault<z.ZodEnum<{
|
|
11
|
+
perspective: "perspective";
|
|
12
|
+
orthographic: "orthographic";
|
|
13
|
+
}>>;
|
|
14
|
+
fov: z.ZodOptional<z.ZodNumber>;
|
|
15
|
+
zoom: z.ZodOptional<z.ZodNumber>;
|
|
16
|
+
}, z.core.$strip>>;
|
|
17
|
+
metadata: z.ZodDefault<z.ZodOptional<z.ZodJSONSchema>>;
|
|
18
|
+
id: z.ZodDefault<z.ZodTemplateLiteral<`window_${string}`>>;
|
|
19
|
+
type: z.ZodDefault<z.ZodLiteral<"window">>;
|
|
20
|
+
position: z.ZodDefault<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
|
|
21
|
+
rotation: z.ZodDefault<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
|
|
22
|
+
side: z.ZodOptional<z.ZodEnum<{
|
|
23
|
+
front: "front";
|
|
24
|
+
back: "back";
|
|
25
|
+
}>>;
|
|
26
|
+
wallId: z.ZodOptional<z.ZodString>;
|
|
27
|
+
width: z.ZodDefault<z.ZodNumber>;
|
|
28
|
+
height: z.ZodDefault<z.ZodNumber>;
|
|
29
|
+
frameThickness: z.ZodDefault<z.ZodNumber>;
|
|
30
|
+
frameDepth: z.ZodDefault<z.ZodNumber>;
|
|
31
|
+
columnRatios: z.ZodDefault<z.ZodArray<z.ZodNumber>>;
|
|
32
|
+
rowRatios: z.ZodDefault<z.ZodArray<z.ZodNumber>>;
|
|
33
|
+
columnDividerThickness: z.ZodDefault<z.ZodNumber>;
|
|
34
|
+
rowDividerThickness: z.ZodDefault<z.ZodNumber>;
|
|
35
|
+
sill: z.ZodDefault<z.ZodBoolean>;
|
|
36
|
+
sillDepth: z.ZodDefault<z.ZodNumber>;
|
|
37
|
+
sillThickness: z.ZodDefault<z.ZodNumber>;
|
|
38
|
+
}, z.core.$strip>;
|
|
39
|
+
export type WindowNode = z.infer<typeof WindowNode>;
|
|
40
|
+
//# sourceMappingURL=window.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"window.d.ts","sourceRoot":"","sources":["../../../src/schema/nodes/window.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwCrB,CAAA;AAEF,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAA"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import dedent from 'dedent';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { BaseNode, nodeType, objectId } from '../base';
|
|
4
|
+
export const WindowNode = BaseNode.extend({
|
|
5
|
+
id: objectId('window'),
|
|
6
|
+
type: nodeType('window'),
|
|
7
|
+
// Position in wall-local coordinate system (center of window)
|
|
8
|
+
position: z.tuple([z.number(), z.number(), z.number()]).default([0, 0, 0]),
|
|
9
|
+
rotation: z.tuple([z.number(), z.number(), z.number()]).default([0, 0, 0]),
|
|
10
|
+
side: z.enum(['front', 'back']).optional(),
|
|
11
|
+
// Wall reference
|
|
12
|
+
wallId: z.string().optional(),
|
|
13
|
+
// Overall dimensions
|
|
14
|
+
width: z.number().default(1.5),
|
|
15
|
+
height: z.number().default(1.5),
|
|
16
|
+
// Frame
|
|
17
|
+
frameThickness: z.number().default(0.05),
|
|
18
|
+
frameDepth: z.number().default(0.07),
|
|
19
|
+
// Divisions — ratios allow non-uniform panes
|
|
20
|
+
// [0.5, 0.5] = two equal panes
|
|
21
|
+
// [0.6, 0.4] = one larger, one smaller
|
|
22
|
+
// [1] = single pane (no division)
|
|
23
|
+
columnRatios: z.array(z.number()).default([1]),
|
|
24
|
+
rowRatios: z.array(z.number()).default([1]),
|
|
25
|
+
columnDividerThickness: z.number().default(0.03),
|
|
26
|
+
rowDividerThickness: z.number().default(0.03),
|
|
27
|
+
// Sill
|
|
28
|
+
sill: z.boolean().default(true),
|
|
29
|
+
sillDepth: z.number().default(0.08),
|
|
30
|
+
sillThickness: z.number().default(0.03),
|
|
31
|
+
}).describe(dedent `Window node - a parametric window placed on a wall
|
|
32
|
+
- position: center of the window in wall-local coordinate system
|
|
33
|
+
- width/height: overall outer dimensions
|
|
34
|
+
- frameThickness: width of the frame members
|
|
35
|
+
- frameDepth: how deep the frame sits within the wall
|
|
36
|
+
- columnRatios/rowRatios: pane division ratios
|
|
37
|
+
- sill: whether to show a window sill
|
|
38
|
+
`);
|
package/dist/schema/types.d.ts
CHANGED
|
@@ -66,6 +66,7 @@ export declare const AnyNode: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
66
66
|
front: "front";
|
|
67
67
|
back: "back";
|
|
68
68
|
}>>;
|
|
69
|
+
children: z.ZodDefault<z.ZodArray<z.ZodDefault<z.ZodTemplateLiteral<`item_${string}`>>>>;
|
|
69
70
|
wallId: z.ZodOptional<z.ZodString>;
|
|
70
71
|
wallT: z.ZodOptional<z.ZodNumber>;
|
|
71
72
|
asset: z.ZodObject<{
|
|
@@ -80,9 +81,13 @@ export declare const AnyNode: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
80
81
|
"wall-side": "wall-side";
|
|
81
82
|
ceiling: "ceiling";
|
|
82
83
|
}>>;
|
|
84
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
83
85
|
offset: z.ZodDefault<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
|
|
84
86
|
rotation: z.ZodDefault<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
|
|
85
87
|
scale: z.ZodDefault<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
|
|
88
|
+
surface: z.ZodOptional<z.ZodObject<{
|
|
89
|
+
height: z.ZodNumber;
|
|
90
|
+
}, z.core.$strip>>;
|
|
86
91
|
}, z.core.$strip>;
|
|
87
92
|
}, z.core.$strip>], "type">>>;
|
|
88
93
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -183,6 +188,7 @@ export declare const AnyNode: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
183
188
|
front: "front";
|
|
184
189
|
back: "back";
|
|
185
190
|
}>>;
|
|
191
|
+
children: z.ZodDefault<z.ZodArray<z.ZodDefault<z.ZodTemplateLiteral<`item_${string}`>>>>;
|
|
186
192
|
wallId: z.ZodOptional<z.ZodString>;
|
|
187
193
|
wallT: z.ZodOptional<z.ZodNumber>;
|
|
188
194
|
asset: z.ZodObject<{
|
|
@@ -197,9 +203,13 @@ export declare const AnyNode: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
197
203
|
"wall-side": "wall-side";
|
|
198
204
|
ceiling: "ceiling";
|
|
199
205
|
}>>;
|
|
206
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
200
207
|
offset: z.ZodDefault<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
|
|
201
208
|
rotation: z.ZodDefault<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
|
|
202
209
|
scale: z.ZodDefault<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
|
|
210
|
+
surface: z.ZodOptional<z.ZodObject<{
|
|
211
|
+
height: z.ZodNumber;
|
|
212
|
+
}, z.core.$strip>>;
|
|
203
213
|
}, z.core.$strip>;
|
|
204
214
|
}, z.core.$strip>, z.ZodObject<{
|
|
205
215
|
object: z.ZodDefault<z.ZodLiteral<"node">>;
|
|
@@ -240,6 +250,7 @@ export declare const AnyNode: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
240
250
|
id: z.ZodDefault<z.ZodTemplateLiteral<`slab_${string}`>>;
|
|
241
251
|
type: z.ZodDefault<z.ZodLiteral<"slab">>;
|
|
242
252
|
polygon: z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>>;
|
|
253
|
+
holes: z.ZodDefault<z.ZodArray<z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>>>>;
|
|
243
254
|
elevation: z.ZodDefault<z.ZodNumber>;
|
|
244
255
|
}, z.core.$strip>, z.ZodObject<{
|
|
245
256
|
object: z.ZodDefault<z.ZodLiteral<"node">>;
|
|
@@ -261,6 +272,7 @@ export declare const AnyNode: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
261
272
|
type: z.ZodDefault<z.ZodLiteral<"ceiling">>;
|
|
262
273
|
children: z.ZodDefault<z.ZodArray<z.ZodDefault<z.ZodTemplateLiteral<`item_${string}`>>>>;
|
|
263
274
|
polygon: z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>>;
|
|
275
|
+
holes: z.ZodDefault<z.ZodArray<z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>>>>;
|
|
264
276
|
height: z.ZodDefault<z.ZodNumber>;
|
|
265
277
|
}, z.core.$strip>, z.ZodObject<{
|
|
266
278
|
object: z.ZodDefault<z.ZodLiteral<"node">>;
|
|
@@ -332,6 +344,42 @@ export declare const AnyNode: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
332
344
|
rotation: z.ZodDefault<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
|
|
333
345
|
scale: z.ZodDefault<z.ZodNumber>;
|
|
334
346
|
opacity: z.ZodDefault<z.ZodNumber>;
|
|
347
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
348
|
+
object: z.ZodDefault<z.ZodLiteral<"node">>;
|
|
349
|
+
name: z.ZodOptional<z.ZodString>;
|
|
350
|
+
parentId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
351
|
+
visible: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
352
|
+
camera: z.ZodOptional<z.ZodObject<{
|
|
353
|
+
position: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
|
|
354
|
+
target: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
|
|
355
|
+
mode: z.ZodDefault<z.ZodEnum<{
|
|
356
|
+
perspective: "perspective";
|
|
357
|
+
orthographic: "orthographic";
|
|
358
|
+
}>>;
|
|
359
|
+
fov: z.ZodOptional<z.ZodNumber>;
|
|
360
|
+
zoom: z.ZodOptional<z.ZodNumber>;
|
|
361
|
+
}, z.core.$strip>>;
|
|
362
|
+
metadata: z.ZodDefault<z.ZodOptional<z.ZodJSONSchema>>;
|
|
363
|
+
id: z.ZodDefault<z.ZodTemplateLiteral<`window_${string}`>>;
|
|
364
|
+
type: z.ZodDefault<z.ZodLiteral<"window">>;
|
|
365
|
+
position: z.ZodDefault<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
|
|
366
|
+
rotation: z.ZodDefault<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
|
|
367
|
+
side: z.ZodOptional<z.ZodEnum<{
|
|
368
|
+
front: "front";
|
|
369
|
+
back: "back";
|
|
370
|
+
}>>;
|
|
371
|
+
wallId: z.ZodOptional<z.ZodString>;
|
|
372
|
+
width: z.ZodDefault<z.ZodNumber>;
|
|
373
|
+
height: z.ZodDefault<z.ZodNumber>;
|
|
374
|
+
frameThickness: z.ZodDefault<z.ZodNumber>;
|
|
375
|
+
frameDepth: z.ZodDefault<z.ZodNumber>;
|
|
376
|
+
columnRatios: z.ZodDefault<z.ZodArray<z.ZodNumber>>;
|
|
377
|
+
rowRatios: z.ZodDefault<z.ZodArray<z.ZodNumber>>;
|
|
378
|
+
columnDividerThickness: z.ZodDefault<z.ZodNumber>;
|
|
379
|
+
rowDividerThickness: z.ZodDefault<z.ZodNumber>;
|
|
380
|
+
sill: z.ZodDefault<z.ZodBoolean>;
|
|
381
|
+
sillDepth: z.ZodDefault<z.ZodNumber>;
|
|
382
|
+
sillThickness: z.ZodDefault<z.ZodNumber>;
|
|
335
383
|
}, z.core.$strip>], "type">;
|
|
336
384
|
export type AnyNode = z.infer<typeof AnyNode>;
|
|
337
385
|
export type AnyNodeType = AnyNode['type'];
|
|
@@ -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;AAcnB,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAalB,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
|
@@ -9,6 +9,7 @@ import { ScanNode } from './nodes/scan';
|
|
|
9
9
|
import { SiteNode } from './nodes/site';
|
|
10
10
|
import { SlabNode } from './nodes/slab';
|
|
11
11
|
import { WallNode } from './nodes/wall';
|
|
12
|
+
import { WindowNode } from './nodes/window';
|
|
12
13
|
import { ZoneNode } from './nodes/zone';
|
|
13
14
|
export const AnyNode = z.discriminatedUnion('type', [
|
|
14
15
|
SiteNode,
|
|
@@ -22,4 +23,5 @@ export const AnyNode = z.discriminatedUnion('type', [
|
|
|
22
23
|
RoofNode,
|
|
23
24
|
ScanNode,
|
|
24
25
|
GuideNode,
|
|
26
|
+
WindowNode,
|
|
25
27
|
]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ceiling-system.d.ts","sourceRoot":"","sources":["../../../src/systems/ceiling/ceiling-system.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,EAAa,WAAW,EAAE,MAAM,cAAc,CAAA;AAO1D,eAAO,MAAM,aAAa,YAuBzB,CAAA;AAeD;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,WAAW,GAAG,KAAK,CAAC,cAAc,
|
|
1
|
+
{"version":3,"file":"ceiling-system.d.ts","sourceRoot":"","sources":["../../../src/systems/ceiling/ceiling-system.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,EAAa,WAAW,EAAE,MAAM,cAAc,CAAA;AAO1D,eAAO,MAAM,aAAa,YAuBzB,CAAA;AAeD;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,WAAW,GAAG,KAAK,CAAC,cAAc,CA+CtF"}
|
|
@@ -56,6 +56,21 @@ export function generateCeilingGeometry(ceilingNode) {
|
|
|
56
56
|
shape.lineTo(pt[0], -pt[1]);
|
|
57
57
|
}
|
|
58
58
|
shape.closePath();
|
|
59
|
+
// Add holes to the shape
|
|
60
|
+
const holes = ceilingNode.holes || [];
|
|
61
|
+
for (const holePolygon of holes) {
|
|
62
|
+
if (holePolygon.length < 3)
|
|
63
|
+
continue;
|
|
64
|
+
const holePath = new THREE.Path();
|
|
65
|
+
const holeFirstPt = holePolygon[0];
|
|
66
|
+
holePath.moveTo(holeFirstPt[0], -holeFirstPt[1]);
|
|
67
|
+
for (let i = 1; i < holePolygon.length; i++) {
|
|
68
|
+
const pt = holePolygon[i];
|
|
69
|
+
holePath.lineTo(pt[0], -pt[1]);
|
|
70
|
+
}
|
|
71
|
+
holePath.closePath();
|
|
72
|
+
shape.holes.push(holePath);
|
|
73
|
+
}
|
|
59
74
|
// Create flat shape geometry (no extrusion)
|
|
60
75
|
const geometry = new THREE.ShapeGeometry(shape);
|
|
61
76
|
// Rotate so the shape lies flat in X-Z plane
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"item-system.d.ts","sourceRoot":"","sources":["../../../src/systems/item/item-system.tsx"],"names":[],"mappings":"AAYA,eAAO,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"item-system.d.ts","sourceRoot":"","sources":["../../../src/systems/item/item-system.tsx"],"names":[],"mappings":"AAYA,eAAO,MAAM,UAAU,YA6CtB,CAAA"}
|
|
@@ -31,10 +31,14 @@ export const ItemSystem = () => {
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
else if (!item.asset.attachTo) {
|
|
34
|
-
//
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
// If parented to another item (surface placement), R3F handles positioning via the hierarchy
|
|
35
|
+
const parentNode = item.parentId ? nodes[item.parentId] : undefined;
|
|
36
|
+
if (parentNode?.type !== 'item') {
|
|
37
|
+
// Floor item: elevate by slab height (using full footprint overlap)
|
|
38
|
+
const levelId = resolveLevelId(item, nodes);
|
|
39
|
+
const slabElevation = spatialGridManager.getSlabElevationForItem(levelId, item.position, item.asset.dimensions, item.rotation);
|
|
40
|
+
mesh.position.y = slabElevation + item.position[1];
|
|
41
|
+
}
|
|
38
42
|
}
|
|
39
43
|
clearDirty(id);
|
|
40
44
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slab-system.d.ts","sourceRoot":"","sources":["../../../src/systems/slab/slab-system.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,EAAa,QAAQ,EAAE,MAAM,cAAc,CAAA;AAOvD,eAAO,MAAM,UAAU,YAwBtB,CAAA;AAkED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,GAAG,KAAK,CAAC,cAAc,
|
|
1
|
+
{"version":3,"file":"slab-system.d.ts","sourceRoot":"","sources":["../../../src/systems/slab/slab-system.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,EAAa,QAAQ,EAAE,MAAM,cAAc,CAAA;AAOvD,eAAO,MAAM,UAAU,YAwBtB,CAAA;AAkED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,GAAG,KAAK,CAAC,cAAc,CAmD7E"}
|
|
@@ -105,6 +105,21 @@ export function generateSlabGeometry(slabNode) {
|
|
|
105
105
|
shape.lineTo(pt[0], -pt[1]);
|
|
106
106
|
}
|
|
107
107
|
shape.closePath();
|
|
108
|
+
// Add holes to the shape
|
|
109
|
+
const holes = slabNode.holes || [];
|
|
110
|
+
for (const holePolygon of holes) {
|
|
111
|
+
if (holePolygon.length < 3)
|
|
112
|
+
continue;
|
|
113
|
+
const holePath = new THREE.Path();
|
|
114
|
+
const holeFirstPt = holePolygon[0];
|
|
115
|
+
holePath.moveTo(holeFirstPt[0], -holeFirstPt[1]);
|
|
116
|
+
for (let i = 1; i < holePolygon.length; i++) {
|
|
117
|
+
const pt = holePolygon[i];
|
|
118
|
+
holePath.lineTo(pt[0], -pt[1]);
|
|
119
|
+
}
|
|
120
|
+
holePath.closePath();
|
|
121
|
+
shape.holes.push(holePath);
|
|
122
|
+
}
|
|
108
123
|
// Extrude the shape by elevation
|
|
109
124
|
const geometry = new THREE.ExtrudeGeometry(shape, {
|
|
110
125
|
depth: elevation,
|
|
@@ -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;
|
|
1
|
+
{"version":3,"file":"wall-system.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-system.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAM9B,OAAO,KAAK,EAAE,OAAO,EAAa,QAAQ,EAAE,MAAM,cAAc,CAAA;AAEhE,OAAO,EAKL,KAAK,aAAa,EACnB,MAAM,iBAAiB,CAAA;AAUxB,eAAO,MAAM,UAAU,YAuDtB,CAAA;AA2DD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,QAAQ,EAClB,aAAa,EAAE,OAAO,EAAE,EACxB,SAAS,EAAE,aAAa,EACxB,aAAa,SAAI,oFA4IlB"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useFrame } from '@react-three/fiber';
|
|
2
2
|
import * as THREE from 'three';
|
|
3
|
+
import { computeBoundsTree } from 'three-mesh-bvh';
|
|
3
4
|
import { Brush, Evaluator, SUBTRACTION } from 'three-bvh-csg';
|
|
4
5
|
import { sceneRegistry } from '../../hooks/scene-registry/scene-registry';
|
|
5
6
|
import { spatialGridManager } from '../../hooks/spatial-grid/spatial-grid-manager';
|
|
@@ -11,6 +12,7 @@ const csgEvaluator = new Evaluator();
|
|
|
11
12
|
// ============================================================================
|
|
12
13
|
// WALL SYSTEM
|
|
13
14
|
// ============================================================================
|
|
15
|
+
let useFrameNb = 0;
|
|
14
16
|
export const WallSystem = () => {
|
|
15
17
|
const dirtyNodes = useScene((state) => state.dirtyNodes);
|
|
16
18
|
const clearDirty = useScene((state) => state.clearDirty);
|
|
@@ -20,11 +22,11 @@ export const WallSystem = () => {
|
|
|
20
22
|
const nodes = useScene.getState().nodes;
|
|
21
23
|
// Collect dirty walls and their levels
|
|
22
24
|
const dirtyWallsByLevel = new Map();
|
|
25
|
+
useFrameNb += 1;
|
|
23
26
|
dirtyNodes.forEach((id) => {
|
|
24
27
|
const node = nodes[id];
|
|
25
28
|
if (!node || node.type !== 'wall')
|
|
26
29
|
return;
|
|
27
|
-
console.log('wall front/back', node.frontSide, node.backSide);
|
|
28
30
|
const levelId = node.parentId;
|
|
29
31
|
if (!levelId)
|
|
30
32
|
return;
|
|
@@ -104,7 +106,7 @@ function updateWallGeometry(wallId, miterData) {
|
|
|
104
106
|
collisionMesh.geometry.dispose();
|
|
105
107
|
collisionMesh.geometry = collisionGeo;
|
|
106
108
|
}
|
|
107
|
-
mesh.position.set(node.start[0],
|
|
109
|
+
mesh.position.set(node.start[0], slabElevation, node.start[1]);
|
|
108
110
|
const angle = Math.atan2(node.end[1] - node.start[1], node.end[0] - node.start[0]);
|
|
109
111
|
mesh.rotation.y = -angle;
|
|
110
112
|
}
|
|
@@ -118,8 +120,10 @@ export function generateExtrudedWall(wallNode, childrenNodes, miterData, slabEle
|
|
|
118
120
|
const { junctionData } = miterData;
|
|
119
121
|
const wallStart = { x: wallNode.start[0], y: wallNode.start[1] };
|
|
120
122
|
const wallEnd = { x: wallNode.end[0], y: wallNode.end[1] };
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
+
// Positive slab: shift the whole wall up (full height preserved)
|
|
124
|
+
// Negative slab: extend wall downward so top stays fixed at wallNode.height
|
|
125
|
+
const wallHeight = wallNode.height ?? 2.5;
|
|
126
|
+
const height = slabElevation > 0 ? wallHeight : wallHeight - slabElevation;
|
|
123
127
|
const thickness = wallNode.thickness ?? 0.1;
|
|
124
128
|
const halfT = thickness / 2;
|
|
125
129
|
// Wall direction and normal (exactly like demo)
|
|
@@ -199,10 +203,6 @@ export function generateExtrudedWall(wallNode, childrenNodes, miterData, slabEle
|
|
|
199
203
|
});
|
|
200
204
|
// Rotate so extrusion direction (Z) becomes height direction (Y)
|
|
201
205
|
geometry.rotateX(-Math.PI / 2);
|
|
202
|
-
// Translate by slab elevation (works for both positive and negative values)
|
|
203
|
-
if (slabElevation !== 0) {
|
|
204
|
-
geometry.translate(0, slabElevation, 0);
|
|
205
|
-
}
|
|
206
206
|
geometry.computeVertexNormals();
|
|
207
207
|
// Apply CSG subtraction for cutouts (doors/windows)
|
|
208
208
|
const cutoutBrushes = collectCutoutBrushes(wallNode, childrenNodes, thickness);
|
|
@@ -210,6 +210,9 @@ export function generateExtrudedWall(wallNode, childrenNodes, miterData, slabEle
|
|
|
210
210
|
return geometry;
|
|
211
211
|
}
|
|
212
212
|
// Create wall brush from geometry
|
|
213
|
+
// Pre-compute BVH with new API to avoid deprecation warning
|
|
214
|
+
geometry.computeBoundsTree = computeBoundsTree;
|
|
215
|
+
geometry.computeBoundsTree({ maxLeafSize: 10 });
|
|
213
216
|
const wallBrush = new Brush(geometry);
|
|
214
217
|
wallBrush.updateMatrixWorld();
|
|
215
218
|
// Subtract each cutout from the wall
|
|
@@ -244,7 +247,7 @@ function collectCutoutBrushes(wallNode, childrenNodes, wallThickness) {
|
|
|
244
247
|
wallMesh.updateMatrixWorld();
|
|
245
248
|
const wallMatrixInverse = wallMesh.matrixWorld.clone().invert();
|
|
246
249
|
for (const child of childrenNodes) {
|
|
247
|
-
if (child.type !== 'item')
|
|
250
|
+
if (child.type !== 'item' && child.type !== 'window')
|
|
248
251
|
continue;
|
|
249
252
|
const childMesh = sceneRegistry.nodes.get(child.id);
|
|
250
253
|
if (!childMesh)
|
|
@@ -279,6 +282,9 @@ function collectCutoutBrushes(wallNode, childrenNodes, wallThickness) {
|
|
|
279
282
|
const boxGeo = new THREE.BoxGeometry(width, height, depth);
|
|
280
283
|
// Position box at the center of the cutout
|
|
281
284
|
boxGeo.translate(minX + width / 2, minY + height / 2, 0);
|
|
285
|
+
// Pre-compute BVH with new API to avoid deprecation warning
|
|
286
|
+
boxGeo.computeBoundsTree = computeBoundsTree;
|
|
287
|
+
boxGeo.computeBoundsTree({ maxLeafSize: 10 });
|
|
282
288
|
const brush = new Brush(boxGeo);
|
|
283
289
|
brushes.push(brush);
|
|
284
290
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"window-system.d.ts","sourceRoot":"","sources":["../../../src/systems/window/window-system.tsx"],"names":[],"mappings":"AA4BA,eAAO,MAAM,YAAY,YA2BxB,CAAA"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { useFrame } from '@react-three/fiber';
|
|
2
|
+
import * as THREE from 'three';
|
|
3
|
+
import { DoubleSide, MeshStandardNodeMaterial } from 'three/webgpu';
|
|
4
|
+
import { sceneRegistry } from '../../hooks/scene-registry/scene-registry';
|
|
5
|
+
import useScene from '../../store/use-scene';
|
|
6
|
+
const glassMaterial = new MeshStandardNodeMaterial({
|
|
7
|
+
name: 'glass',
|
|
8
|
+
color: 'lightblue',
|
|
9
|
+
roughness: 0.05,
|
|
10
|
+
metalness: 0.1,
|
|
11
|
+
transparent: true,
|
|
12
|
+
opacity: 0.3,
|
|
13
|
+
side: DoubleSide,
|
|
14
|
+
depthWrite: false,
|
|
15
|
+
});
|
|
16
|
+
const frameMaterial = new MeshStandardNodeMaterial({
|
|
17
|
+
name: 'window-frame',
|
|
18
|
+
color: '#e8e8e8',
|
|
19
|
+
roughness: 0.6,
|
|
20
|
+
metalness: 0,
|
|
21
|
+
});
|
|
22
|
+
// Invisible material for root mesh — used as selection hitbox only
|
|
23
|
+
const hitboxMaterial = new THREE.MeshBasicMaterial({ visible: false });
|
|
24
|
+
export const WindowSystem = () => {
|
|
25
|
+
const dirtyNodes = useScene((state) => state.dirtyNodes);
|
|
26
|
+
const clearDirty = useScene((state) => state.clearDirty);
|
|
27
|
+
useFrame(() => {
|
|
28
|
+
if (dirtyNodes.size === 0)
|
|
29
|
+
return;
|
|
30
|
+
const nodes = useScene.getState().nodes;
|
|
31
|
+
dirtyNodes.forEach((id) => {
|
|
32
|
+
const node = nodes[id];
|
|
33
|
+
if (!node || node.type !== 'window')
|
|
34
|
+
return;
|
|
35
|
+
const mesh = sceneRegistry.nodes.get(id);
|
|
36
|
+
if (!mesh)
|
|
37
|
+
return; // Keep dirty until mesh mounts
|
|
38
|
+
updateWindowMesh(node, mesh);
|
|
39
|
+
clearDirty(id);
|
|
40
|
+
// Rebuild the parent wall so its cutout reflects the updated window geometry
|
|
41
|
+
if (node.parentId) {
|
|
42
|
+
useScene.getState().dirtyNodes.add(node.parentId);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
return null;
|
|
47
|
+
};
|
|
48
|
+
function addBox(parent, material, w, h, d, x, y, z) {
|
|
49
|
+
const m = new THREE.Mesh(new THREE.BoxGeometry(w, h, d), material);
|
|
50
|
+
m.position.set(x, y, z);
|
|
51
|
+
parent.add(m);
|
|
52
|
+
}
|
|
53
|
+
function updateWindowMesh(node, mesh) {
|
|
54
|
+
// Root mesh is an invisible hitbox; all visuals live in child meshes
|
|
55
|
+
mesh.geometry.dispose();
|
|
56
|
+
mesh.geometry = new THREE.BoxGeometry(node.width, node.height, node.frameDepth);
|
|
57
|
+
mesh.material = hitboxMaterial;
|
|
58
|
+
// Sync transform from node (React may lag behind the system by a frame during drag)
|
|
59
|
+
mesh.position.set(node.position[0], node.position[1], node.position[2]);
|
|
60
|
+
mesh.rotation.set(node.rotation[0], node.rotation[1], node.rotation[2]);
|
|
61
|
+
// Dispose and remove all old visual children; preserve 'cutout'
|
|
62
|
+
for (const child of [...mesh.children]) {
|
|
63
|
+
if (child.name === 'cutout')
|
|
64
|
+
continue;
|
|
65
|
+
if (child instanceof THREE.Mesh)
|
|
66
|
+
child.geometry.dispose();
|
|
67
|
+
mesh.remove(child);
|
|
68
|
+
}
|
|
69
|
+
const { width, height, frameDepth, frameThickness, columnRatios, rowRatios, columnDividerThickness, rowDividerThickness, sill, sillDepth, sillThickness, } = node;
|
|
70
|
+
const innerW = width - 2 * frameThickness;
|
|
71
|
+
const innerH = height - 2 * frameThickness;
|
|
72
|
+
// ── Frame members ──
|
|
73
|
+
// Top / bottom — full width
|
|
74
|
+
addBox(mesh, frameMaterial, width, frameThickness, frameDepth, 0, height / 2 - frameThickness / 2, 0);
|
|
75
|
+
addBox(mesh, frameMaterial, width, frameThickness, frameDepth, 0, -height / 2 + frameThickness / 2, 0);
|
|
76
|
+
// Left / right — inner height to avoid corner overlap
|
|
77
|
+
addBox(mesh, frameMaterial, frameThickness, innerH, frameDepth, -width / 2 + frameThickness / 2, 0, 0);
|
|
78
|
+
addBox(mesh, frameMaterial, frameThickness, innerH, frameDepth, width / 2 - frameThickness / 2, 0, 0);
|
|
79
|
+
// ── Pane grid ──
|
|
80
|
+
const numCols = columnRatios.length;
|
|
81
|
+
const numRows = rowRatios.length;
|
|
82
|
+
const usableW = innerW - (numCols - 1) * columnDividerThickness;
|
|
83
|
+
const usableH = innerH - (numRows - 1) * rowDividerThickness;
|
|
84
|
+
const colSum = columnRatios.reduce((a, b) => a + b, 0);
|
|
85
|
+
const rowSum = rowRatios.reduce((a, b) => a + b, 0);
|
|
86
|
+
const colWidths = columnRatios.map(r => (r / colSum) * usableW);
|
|
87
|
+
const rowHeights = rowRatios.map(r => (r / rowSum) * usableH);
|
|
88
|
+
// Compute column x-centers starting from left edge of inner area
|
|
89
|
+
const colXCenters = [];
|
|
90
|
+
let cx = -innerW / 2;
|
|
91
|
+
for (let c = 0; c < numCols; c++) {
|
|
92
|
+
colXCenters.push(cx + colWidths[c] / 2);
|
|
93
|
+
cx += colWidths[c];
|
|
94
|
+
if (c < numCols - 1)
|
|
95
|
+
cx += columnDividerThickness;
|
|
96
|
+
}
|
|
97
|
+
// Compute row y-centers starting from top edge of inner area (R1 = top)
|
|
98
|
+
const rowYCenters = [];
|
|
99
|
+
let cy = innerH / 2;
|
|
100
|
+
for (let r = 0; r < numRows; r++) {
|
|
101
|
+
rowYCenters.push(cy - rowHeights[r] / 2);
|
|
102
|
+
cy -= rowHeights[r];
|
|
103
|
+
if (r < numRows - 1)
|
|
104
|
+
cy -= rowDividerThickness;
|
|
105
|
+
}
|
|
106
|
+
// Column dividers — full inner height
|
|
107
|
+
cx = -innerW / 2;
|
|
108
|
+
for (let c = 0; c < numCols - 1; c++) {
|
|
109
|
+
cx += colWidths[c];
|
|
110
|
+
addBox(mesh, frameMaterial, columnDividerThickness, innerH, frameDepth, cx + columnDividerThickness / 2, 0, 0);
|
|
111
|
+
cx += columnDividerThickness;
|
|
112
|
+
}
|
|
113
|
+
// Row dividers — per column width, so they don't overlap column dividers (top to bottom)
|
|
114
|
+
cy = innerH / 2;
|
|
115
|
+
for (let r = 0; r < numRows - 1; r++) {
|
|
116
|
+
cy -= rowHeights[r];
|
|
117
|
+
const divY = cy - rowDividerThickness / 2;
|
|
118
|
+
for (let c = 0; c < numCols; c++) {
|
|
119
|
+
addBox(mesh, frameMaterial, colWidths[c], rowDividerThickness, frameDepth, colXCenters[c], divY, 0);
|
|
120
|
+
}
|
|
121
|
+
cy -= rowDividerThickness;
|
|
122
|
+
}
|
|
123
|
+
// Glass panes
|
|
124
|
+
const glassDepth = Math.max(0.004, frameDepth * 0.08);
|
|
125
|
+
for (let c = 0; c < numCols; c++) {
|
|
126
|
+
for (let r = 0; r < numRows; r++) {
|
|
127
|
+
addBox(mesh, glassMaterial, colWidths[c], rowHeights[r], glassDepth, colXCenters[c], rowYCenters[r], 0);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// ── Sill ──
|
|
131
|
+
if (sill) {
|
|
132
|
+
const sillW = width + sillDepth * 0.4; // slightly wider than frame
|
|
133
|
+
// Protrudes from the front face of the frame (+Z)
|
|
134
|
+
const sillZ = frameDepth / 2 + sillDepth / 2;
|
|
135
|
+
addBox(mesh, frameMaterial, sillW, sillThickness, sillDepth, 0, -height / 2 - sillThickness / 2, sillZ);
|
|
136
|
+
}
|
|
137
|
+
// ── Cutout (for wall CSG) — always full window dimensions, 1m deep ──
|
|
138
|
+
let cutout = mesh.getObjectByName('cutout');
|
|
139
|
+
if (!cutout) {
|
|
140
|
+
cutout = new THREE.Mesh();
|
|
141
|
+
cutout.name = 'cutout';
|
|
142
|
+
mesh.add(cutout);
|
|
143
|
+
}
|
|
144
|
+
cutout.geometry.dispose();
|
|
145
|
+
cutout.geometry = new THREE.BoxGeometry(node.width, node.height, 1.0);
|
|
146
|
+
cutout.visible = false;
|
|
147
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pascal-app/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"description": "Core library for Pascal 3D building editor",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"mitt": "^3.0.1",
|
|
34
34
|
"nanoid": "^5.1.6",
|
|
35
35
|
"three-bvh-csg": "^0.0.17",
|
|
36
|
+
"three-mesh-bvh": "^0.9.8",
|
|
36
37
|
"zod": "^4.3.5",
|
|
37
38
|
"zundo": "^2.3.0",
|
|
38
39
|
"zustand": "^5"
|
|
@@ -53,8 +54,10 @@
|
|
|
53
54
|
],
|
|
54
55
|
"repository": {
|
|
55
56
|
"type": "git",
|
|
56
|
-
"url": "https://github.com/
|
|
57
|
+
"url": "https://github.com/pascalorg/editor.git",
|
|
57
58
|
"directory": "packages/core"
|
|
58
59
|
},
|
|
59
|
-
"license": "MIT"
|
|
60
|
+
"license": "MIT",
|
|
61
|
+
"homepage": "https://github.com/pascalorg/editor/tree/main/packages/core#readme",
|
|
62
|
+
"bugs": "https://github.com/pascalorg/editor/issues"
|
|
60
63
|
}
|