@pascal-app/core 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/dist/events/bus.d.ts +42 -0
  2. package/dist/events/bus.d.ts.map +1 -0
  3. package/dist/events/bus.js +13 -0
  4. package/dist/hooks/scene-registry/scene-registry.d.ts +18 -0
  5. package/dist/hooks/scene-registry/scene-registry.d.ts.map +1 -0
  6. package/dist/hooks/scene-registry/scene-registry.js +35 -0
  7. package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts +90 -0
  8. package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -0
  9. package/dist/hooks/spatial-grid/spatial-grid-manager.js +466 -0
  10. package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts +4 -0
  11. package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts.map +1 -0
  12. package/dist/hooks/spatial-grid/spatial-grid-sync.js +115 -0
  13. package/dist/hooks/spatial-grid/spatial-grid.d.ts +23 -0
  14. package/dist/hooks/spatial-grid/spatial-grid.d.ts.map +1 -0
  15. package/dist/hooks/spatial-grid/spatial-grid.js +115 -0
  16. package/dist/hooks/spatial-grid/use-spatial-query.d.ts +16 -0
  17. package/dist/hooks/spatial-grid/use-spatial-query.d.ts.map +1 -0
  18. package/dist/hooks/spatial-grid/use-spatial-query.js +14 -0
  19. package/dist/hooks/spatial-grid/wall-spatial-grid.d.ts +47 -0
  20. package/dist/hooks/spatial-grid/wall-spatial-grid.d.ts.map +1 -0
  21. package/dist/hooks/spatial-grid/wall-spatial-grid.js +113 -0
  22. package/dist/index.d.ts +17 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +22 -0
  25. package/dist/lib/asset-storage.d.ts +11 -0
  26. package/dist/lib/asset-storage.d.ts.map +1 -0
  27. package/dist/lib/asset-storage.js +48 -0
  28. package/dist/lib/space-detection.d.ts +34 -0
  29. package/dist/lib/space-detection.d.ts.map +1 -0
  30. package/dist/lib/space-detection.js +499 -0
  31. package/dist/schema/base.d.ts +30 -0
  32. package/dist/schema/base.d.ts.map +1 -0
  33. package/dist/schema/base.js +25 -0
  34. package/dist/schema/camera.d.ts +13 -0
  35. package/dist/schema/camera.d.ts.map +1 -0
  36. package/dist/schema/camera.js +9 -0
  37. package/dist/schema/index.d.ts +17 -0
  38. package/dist/schema/index.d.ts.map +1 -0
  39. package/dist/schema/index.js +18 -0
  40. package/dist/schema/nodes/building.d.ts +25 -0
  41. package/dist/schema/nodes/building.d.ts.map +1 -0
  42. package/dist/schema/nodes/building.js +16 -0
  43. package/dist/schema/nodes/ceiling.d.ts +25 -0
  44. package/dist/schema/nodes/ceiling.d.ts.map +1 -0
  45. package/dist/schema/nodes/ceiling.js +16 -0
  46. package/dist/schema/nodes/guide.d.ts +27 -0
  47. package/dist/schema/nodes/guide.d.ts.map +1 -0
  48. package/dist/schema/nodes/guide.js +11 -0
  49. package/dist/schema/nodes/item.d.ts +65 -0
  50. package/dist/schema/nodes/item.d.ts.map +1 -0
  51. package/dist/schema/nodes/item.js +38 -0
  52. package/dist/schema/nodes/level.d.ts +24 -0
  53. package/dist/schema/nodes/level.d.ts.map +1 -0
  54. package/dist/schema/nodes/level.js +21 -0
  55. package/dist/schema/nodes/roof.d.ts +28 -0
  56. package/dist/schema/nodes/roof.d.ts.map +1 -0
  57. package/dist/schema/nodes/roof.js +28 -0
  58. package/dist/schema/nodes/scan.d.ts +27 -0
  59. package/dist/schema/nodes/scan.d.ts.map +1 -0
  60. package/dist/schema/nodes/scan.js +11 -0
  61. package/dist/schema/nodes/site.d.ts +90 -0
  62. package/dist/schema/nodes/site.d.ts.map +1 -0
  63. package/dist/schema/nodes/site.js +39 -0
  64. package/dist/schema/nodes/slab.d.ts +24 -0
  65. package/dist/schema/nodes/slab.d.ts.map +1 -0
  66. package/dist/schema/nodes/slab.js +15 -0
  67. package/dist/schema/nodes/wall.d.ts +37 -0
  68. package/dist/schema/nodes/wall.d.ts.map +1 -0
  69. package/dist/schema/nodes/wall.js +30 -0
  70. package/dist/schema/nodes/zone.d.ts +24 -0
  71. package/dist/schema/nodes/zone.d.ts.map +1 -0
  72. package/dist/schema/nodes/zone.js +22 -0
  73. package/dist/schema/types.d.ts +339 -0
  74. package/dist/schema/types.d.ts.map +1 -0
  75. package/dist/schema/types.js +25 -0
  76. package/dist/store/actions/node-actions.d.ts +12 -0
  77. package/dist/store/actions/node-actions.d.ts.map +1 -0
  78. package/dist/store/actions/node-actions.js +121 -0
  79. package/dist/store/use-scene.d.ts +31 -0
  80. package/dist/store/use-scene.d.ts.map +1 -0
  81. package/dist/store/use-scene.js +127 -0
  82. package/dist/systems/ceiling/ceiling-system.d.ts +8 -0
  83. package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -0
  84. package/dist/systems/ceiling/ceiling-system.js +65 -0
  85. package/dist/systems/item/item-system.d.ts +2 -0
  86. package/dist/systems/item/item-system.d.ts.map +1 -0
  87. package/dist/systems/item/item-system.js +43 -0
  88. package/dist/systems/roof/roof-system.d.ts +8 -0
  89. package/dist/systems/roof/roof-system.d.ts.map +1 -0
  90. package/dist/systems/roof/roof-system.js +254 -0
  91. package/dist/systems/slab/slab-system.d.ts +8 -0
  92. package/dist/systems/slab/slab-system.d.ts.map +1 -0
  93. package/dist/systems/slab/slab-system.js +117 -0
  94. package/dist/systems/wall/wall-mitering.d.ts +32 -0
  95. package/dist/systems/wall/wall-mitering.d.ts.map +1 -0
  96. package/dist/systems/wall/wall-mitering.js +214 -0
  97. package/dist/systems/wall/wall-system.d.ts +12 -0
  98. package/dist/systems/wall/wall-system.d.ts.map +1 -0
  99. package/dist/systems/wall/wall-system.js +286 -0
  100. package/dist/utils/types.d.ts +6 -0
  101. package/dist/utils/types.d.ts.map +1 -0
  102. package/dist/utils/types.js +7 -0
  103. package/package.json +58 -0
@@ -0,0 +1,115 @@
1
+ export class SpatialGrid {
2
+ config;
3
+ cells = new Map();
4
+ itemCells = new Map(); // reverse lookup
5
+ constructor(config) {
6
+ this.config = config;
7
+ }
8
+ posToCell(x, z) {
9
+ return [Math.floor(x / this.config.cellSize), Math.floor(z / this.config.cellSize)];
10
+ }
11
+ cellKey(cx, cz) {
12
+ return `${cx},${cz}`;
13
+ }
14
+ // Get all cells an item occupies based on its AABB
15
+ getItemCells(position, dimensions, rotation) {
16
+ // Simplified: axis-aligned bounding box
17
+ // For full rotation support, compute rotated corners
18
+ const [x, , z] = position;
19
+ const [w, , d] = dimensions;
20
+ const yRot = rotation[1]; // Y-axis rotation
21
+ // Compute rotated footprint (simplified for 90° increments)
22
+ const cos = Math.abs(Math.cos(yRot));
23
+ const sin = Math.abs(Math.sin(yRot));
24
+ const rotatedW = w * cos + d * sin;
25
+ const rotatedD = w * sin + d * cos;
26
+ const minX = x - rotatedW / 2;
27
+ const maxX = x + rotatedW / 2;
28
+ const minZ = z - rotatedD / 2;
29
+ const maxZ = z + rotatedD / 2;
30
+ const [minCx, minCz] = this.posToCell(minX, minZ);
31
+ // Use exclusive upper bound: subtract epsilon so exact boundaries don't overlap
32
+ // This allows adjacent items (touching but not overlapping) to not conflict
33
+ const epsilon = 1e-6;
34
+ const [maxCx, maxCz] = this.posToCell(maxX - epsilon, maxZ - epsilon);
35
+ const keys = [];
36
+ for (let cx = minCx; cx <= maxCx; cx++) {
37
+ for (let cz = minCz; cz <= maxCz; cz++) {
38
+ keys.push(this.cellKey(cx, cz));
39
+ }
40
+ }
41
+ return keys;
42
+ }
43
+ // Register an item
44
+ insert(itemId, position, dimensions, rotation) {
45
+ const cellKeys = this.getItemCells(position, dimensions, rotation);
46
+ this.itemCells.set(itemId, new Set(cellKeys));
47
+ for (const key of cellKeys) {
48
+ if (!this.cells.has(key)) {
49
+ this.cells.set(key, { itemIds: new Set() });
50
+ }
51
+ this.cells.get(key).itemIds.add(itemId);
52
+ }
53
+ }
54
+ // Remove an item
55
+ remove(itemId) {
56
+ const cellKeys = this.itemCells.get(itemId);
57
+ if (!cellKeys)
58
+ return;
59
+ for (const key of cellKeys) {
60
+ const cell = this.cells.get(key);
61
+ if (cell) {
62
+ cell.itemIds.delete(itemId);
63
+ if (cell.itemIds.size === 0) {
64
+ this.cells.delete(key);
65
+ }
66
+ }
67
+ }
68
+ this.itemCells.delete(itemId);
69
+ }
70
+ // Update = remove + insert
71
+ update(itemId, position, dimensions, rotation) {
72
+ this.remove(itemId);
73
+ this.insert(itemId, position, dimensions, rotation);
74
+ }
75
+ // Query: is this placement valid?
76
+ canPlace(position, dimensions, rotation, ignoreIds = []) {
77
+ const cellKeys = this.getItemCells(position, dimensions, rotation);
78
+ const ignoreSet = new Set(ignoreIds);
79
+ const conflicts = new Set();
80
+ for (const key of cellKeys) {
81
+ const cell = this.cells.get(key);
82
+ if (cell) {
83
+ for (const id of cell.itemIds) {
84
+ if (!ignoreSet.has(id)) {
85
+ conflicts.add(id);
86
+ }
87
+ }
88
+ }
89
+ }
90
+ return {
91
+ valid: conflicts.size === 0,
92
+ conflictIds: [...conflicts],
93
+ };
94
+ }
95
+ // Query: get all items near a point (for snapping, selection, etc.)
96
+ queryRadius(x, z, radius) {
97
+ const cellRadius = Math.ceil(radius / this.config.cellSize);
98
+ const [cx, cz] = this.posToCell(x, z);
99
+ const found = new Set();
100
+ for (let dx = -cellRadius; dx <= cellRadius; dx++) {
101
+ for (let dz = -cellRadius; dz <= cellRadius; dz++) {
102
+ const cell = this.cells.get(this.cellKey(cx + dx, cz + dz));
103
+ if (cell) {
104
+ for (const id of cell.itemIds) {
105
+ found.add(id);
106
+ }
107
+ }
108
+ }
109
+ }
110
+ return [...found];
111
+ }
112
+ getItemCount() {
113
+ return this.itemCells.size;
114
+ }
115
+ }
@@ -0,0 +1,16 @@
1
+ import type { CeilingNode, LevelNode, WallNode } from '../../schema';
2
+ export declare function useSpatialQuery(): {
3
+ canPlaceOnFloor: (levelId: LevelNode["id"], position: [number, number, number], dimensions: [number, number, number], rotation: [number, number, number], ignoreIds?: string[]) => {
4
+ valid: boolean;
5
+ conflictIds: string[];
6
+ };
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
+ valid: boolean;
9
+ conflictIds: string[];
10
+ };
11
+ canPlaceOnCeiling: (ceilingId: CeilingNode["id"], position: [number, number, number], dimensions: [number, number, number], rotation: [number, number, number], ignoreIds?: string[]) => {
12
+ valid: boolean;
13
+ conflictIds: string[];
14
+ };
15
+ };
16
+ //# sourceMappingURL=use-spatial-query.d.ts.map
@@ -0,0 +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;;;;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"}
@@ -0,0 +1,14 @@
1
+ import { useCallback } from 'react';
2
+ import { spatialGridManager } from './spatial-grid-manager';
3
+ export function useSpatialQuery() {
4
+ const canPlaceOnFloor = useCallback((levelId, position, dimensions, rotation, ignoreIds) => {
5
+ return spatialGridManager.canPlaceOnFloor(levelId, position, dimensions, rotation, ignoreIds);
6
+ }, []);
7
+ const canPlaceOnWall = useCallback((levelId, wallId, localX, localY, dimensions, attachType = 'wall', side, ignoreIds) => {
8
+ return spatialGridManager.canPlaceOnWall(levelId, wallId, localX, localY, dimensions, attachType, side, ignoreIds);
9
+ }, []);
10
+ const canPlaceOnCeiling = useCallback((ceilingId, position, dimensions, rotation, ignoreIds) => {
11
+ return spatialGridManager.canPlaceOnCeiling(ceilingId, position, dimensions, rotation, ignoreIds);
12
+ }, []);
13
+ return { canPlaceOnFloor, canPlaceOnWall, canPlaceOnCeiling };
14
+ }
@@ -0,0 +1,47 @@
1
+ type WallSide = 'front' | 'back';
2
+ type AttachType = 'wall' | 'wall-side';
3
+ interface WallItemPlacement {
4
+ itemId: string;
5
+ wallId: string;
6
+ tStart: number;
7
+ tEnd: number;
8
+ yStart: number;
9
+ yEnd: number;
10
+ attachType?: AttachType;
11
+ side?: WallSide;
12
+ }
13
+ export declare class WallSpatialGrid {
14
+ private wallItems;
15
+ private itemToWall;
16
+ /**
17
+ * Check if an item can be placed on a wall
18
+ * @param wallId - The wall to place on
19
+ * @param wallLength - Length of the wall
20
+ * @param wallHeight - Height of the wall
21
+ * @param tCenter - Parametric center position (0-1) along wall
22
+ * @param itemWidth - Width of the item
23
+ * @param yBottom - Bottom Y position of the item
24
+ * @param itemHeight - Height of the item
25
+ * @param attachType - 'wall' (blocks both sides) or 'wall-side' (blocks one side)
26
+ * @param side - Which side for 'wall-side' items
27
+ * @param ignoreIds - Item IDs to ignore in conflict check
28
+ */
29
+ canPlaceOnWall(wallId: string, wallLength: number, wallHeight: number, tCenter: number, itemWidth: number, yBottom: number, itemHeight: number, attachType?: AttachType, side?: WallSide, ignoreIds?: string[]): {
30
+ valid: boolean;
31
+ conflictIds: string[];
32
+ };
33
+ /**
34
+ * Check if two items conflict based on their attach types and sides
35
+ * - 'wall' items block both sides, so they conflict with everything
36
+ * - 'wall-side' items only conflict if they're on the same side or if the other is a 'wall' item
37
+ */
38
+ private checkSideConflict;
39
+ insert(placement: WallItemPlacement): void;
40
+ remove(wallId: string, itemId: string): void;
41
+ removeByItemId(itemId: string): void;
42
+ removeWall(wallId: string): string[];
43
+ getWallForItem(itemId: string): string | undefined;
44
+ clear(): void;
45
+ }
46
+ export {};
47
+ //# sourceMappingURL=wall-spatial-grid.d.ts.map
@@ -0,0 +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;AAKtC,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;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,SAAS,CAAyC;IAC1D,OAAO,CAAC,UAAU,CAA4B;IAE9C;;;;;;;;;;;;OAYG;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,CAAA;KAAE;IAoC5C;;;;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"}
@@ -0,0 +1,113 @@
1
+ // Small tolerance for floating point comparison to allow adjacent items
2
+ const EPSILON = 0.001;
3
+ export class WallSpatialGrid {
4
+ wallItems = new Map(); // wallId -> placements
5
+ itemToWall = new Map(); // itemId -> wallId (reverse lookup)
6
+ /**
7
+ * Check if an item can be placed on a wall
8
+ * @param wallId - The wall to place on
9
+ * @param wallLength - Length of the wall
10
+ * @param wallHeight - Height of the wall
11
+ * @param tCenter - Parametric center position (0-1) along wall
12
+ * @param itemWidth - Width of the item
13
+ * @param yBottom - Bottom Y position of the item
14
+ * @param itemHeight - Height of the item
15
+ * @param attachType - 'wall' (blocks both sides) or 'wall-side' (blocks one side)
16
+ * @param side - Which side for 'wall-side' items
17
+ * @param ignoreIds - Item IDs to ignore in conflict check
18
+ */
19
+ canPlaceOnWall(wallId, wallLength, wallHeight, tCenter, itemWidth, yBottom, itemHeight, attachType = 'wall', side, ignoreIds = []) {
20
+ const halfW = itemWidth / wallLength / 2;
21
+ const tStart = tCenter - halfW;
22
+ const tEnd = tCenter + halfW;
23
+ // yBottom is the bottom of the item, so yEnd = yBottom + itemHeight
24
+ const yStart = yBottom;
25
+ const yEnd = yBottom + itemHeight;
26
+ // Check wall boundaries
27
+ if (tStart < 0 || tEnd > 1 || yStart < 0 || yEnd > wallHeight) {
28
+ return { valid: false, conflictIds: [] };
29
+ }
30
+ const existing = this.wallItems.get(wallId) ?? [];
31
+ const ignoreSet = new Set(ignoreIds);
32
+ const conflicts = [];
33
+ for (const placement of existing) {
34
+ if (ignoreSet.has(placement.itemId))
35
+ continue;
36
+ // Use EPSILON tolerance to allow items to be exactly adjacent
37
+ const tOverlap = tStart < placement.tEnd - EPSILON && tEnd > placement.tStart + EPSILON;
38
+ const yOverlap = yStart < placement.yEnd - EPSILON && yEnd > placement.yStart + EPSILON;
39
+ if (tOverlap && yOverlap) {
40
+ // Check side conflicts based on attach types
41
+ const hasConflict = this.checkSideConflict(attachType, side, placement);
42
+ if (hasConflict) {
43
+ conflicts.push(placement.itemId);
44
+ }
45
+ }
46
+ }
47
+ return { valid: conflicts.length === 0, conflictIds: conflicts };
48
+ }
49
+ /**
50
+ * Check if two items conflict based on their attach types and sides
51
+ * - 'wall' items block both sides, so they conflict with everything
52
+ * - 'wall-side' items only conflict if they're on the same side or if the other is a 'wall' item
53
+ */
54
+ checkSideConflict(newAttachType, newSide, existing) {
55
+ // Treat undefined/legacy attachType as 'wall' (blocks both sides)
56
+ const existingAttachType = existing.attachType ?? 'wall';
57
+ // If new item is 'wall' type, it conflicts with everything (needs both sides)
58
+ if (newAttachType === 'wall') {
59
+ return true;
60
+ }
61
+ // If existing item is 'wall' type, it blocks both sides
62
+ if (existingAttachType === 'wall') {
63
+ return true;
64
+ }
65
+ // Both are 'wall-side' - only conflict if they're on the same side
66
+ // If either side is undefined, be conservative and assume conflict
67
+ if (!newSide || !existing.side) {
68
+ return true;
69
+ }
70
+ return newSide === existing.side;
71
+ }
72
+ insert(placement) {
73
+ const { wallId, itemId } = placement;
74
+ if (!this.wallItems.has(wallId)) {
75
+ this.wallItems.set(wallId, []);
76
+ }
77
+ this.wallItems.get(wallId).push(placement);
78
+ this.itemToWall.set(itemId, wallId);
79
+ }
80
+ remove(wallId, itemId) {
81
+ const items = this.wallItems.get(wallId);
82
+ if (items) {
83
+ const idx = items.findIndex((p) => p.itemId === itemId);
84
+ if (idx !== -1)
85
+ items.splice(idx, 1);
86
+ }
87
+ this.itemToWall.delete(itemId);
88
+ }
89
+ removeByItemId(itemId) {
90
+ const wallId = this.itemToWall.get(itemId);
91
+ if (wallId) {
92
+ this.remove(wallId, itemId);
93
+ }
94
+ }
95
+ // Useful for when a wall is deleted - remove all items on it
96
+ removeWall(wallId) {
97
+ const items = this.wallItems.get(wallId) ?? [];
98
+ const removedIds = items.map((p) => p.itemId);
99
+ for (const itemId of removedIds) {
100
+ this.itemToWall.delete(itemId);
101
+ }
102
+ this.wallItems.delete(wallId);
103
+ return removedIds; // Return removed item IDs in case you need to delete them from scene
104
+ }
105
+ // Get which wall an item is on
106
+ getWallForItem(itemId) {
107
+ return this.itemToWall.get(itemId);
108
+ }
109
+ clear() {
110
+ this.wallItems.clear();
111
+ this.itemToWall.clear();
112
+ }
113
+ }
@@ -0,0 +1,17 @@
1
+ export type { BuildingEvent, CameraControlEvent, EventSuffix, GridEvent, ItemEvent, LevelEvent, NodeEvent, SlabEvent, WallEvent, ZoneEvent, CeilingEvent, RoofEvent, } from './events/bus';
2
+ export { emitter, eventSuffixes } from './events/bus';
3
+ export { sceneRegistry, useRegistry, } from './hooks/scene-registry/scene-registry';
4
+ export { initSpatialGridSync, resolveLevelId, } from './hooks/spatial-grid/spatial-grid-sync';
5
+ export { useSpatialQuery } from './hooks/spatial-grid/use-spatial-query';
6
+ export { pointInPolygon } from './hooks/spatial-grid/spatial-grid-manager';
7
+ export * from './schema';
8
+ export { default as useScene } from './store/use-scene';
9
+ export { CeilingSystem } from './systems/ceiling/ceiling-system';
10
+ export { ItemSystem } from './systems/item/item-system';
11
+ export { RoofSystem } from './systems/roof/roof-system';
12
+ export { SlabSystem } from './systems/slab/slab-system';
13
+ export { WallSystem } from './systems/wall/wall-system';
14
+ 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
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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,YAAY,EACZ,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,EACL,mBAAmB,EACnB,cAAc,GACf,MAAM,wCAAwC,CAAA;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,wCAAwC,CAAA;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,2CAA2C,CAAA;AAE1E,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;AAEvD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAExC,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAE7D,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,KAAK,KAAK,EAAE,MAAM,uBAAuB,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,22 @@
1
+ // Store
2
+ // Events
3
+ export { emitter, eventSuffixes } from './events/bus';
4
+ // Hooks
5
+ export { sceneRegistry, useRegistry, } from './hooks/scene-registry/scene-registry';
6
+ export { initSpatialGridSync, resolveLevelId, } from './hooks/spatial-grid/spatial-grid-sync';
7
+ export { useSpatialQuery } from './hooks/spatial-grid/use-spatial-query';
8
+ export { pointInPolygon } from './hooks/spatial-grid/spatial-grid-manager';
9
+ // Schema
10
+ export * from './schema';
11
+ export { default as useScene } from './store/use-scene';
12
+ // Systems
13
+ export { CeilingSystem } from './systems/ceiling/ceiling-system';
14
+ export { ItemSystem } from './systems/item/item-system';
15
+ export { RoofSystem } from './systems/roof/roof-system';
16
+ export { SlabSystem } from './systems/slab/slab-system';
17
+ export { WallSystem } from './systems/wall/wall-system';
18
+ 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';
@@ -0,0 +1,11 @@
1
+ export declare const ASSET_PREFIX = "asset_data:";
2
+ /**
3
+ * Save a file to IndexedDB and return a custom protocol URL
4
+ */
5
+ export declare function saveAsset(file: File): Promise<string>;
6
+ /**
7
+ * Load a file from IndexedDB and return an object URL
8
+ * If the URL is not a custom protocol URL, return it as is
9
+ */
10
+ export declare function loadAssetUrl(url: string): Promise<string | null>;
11
+ //# sourceMappingURL=asset-storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asset-storage.d.ts","sourceRoot":"","sources":["../../src/lib/asset-storage.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY,gBAAgB,CAAA;AAKzC;;GAEG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAI3D;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAkCtE"}
@@ -0,0 +1,48 @@
1
+ import { get, set } from 'idb-keyval';
2
+ export const ASSET_PREFIX = 'asset_data:';
3
+ // Cache for active object URLs to prevent leaks and flickering
4
+ const urlCache = new Map();
5
+ /**
6
+ * Save a file to IndexedDB and return a custom protocol URL
7
+ */
8
+ export async function saveAsset(file) {
9
+ const id = crypto.randomUUID();
10
+ await set(`${ASSET_PREFIX}${id}`, file);
11
+ return `asset://${id}`;
12
+ }
13
+ /**
14
+ * Load a file from IndexedDB and return an object URL
15
+ * If the URL is not a custom protocol URL, return it as is
16
+ */
17
+ export async function loadAssetUrl(url) {
18
+ if (!url)
19
+ return null;
20
+ // If it's already a blob or http URL, return as is
21
+ if (url.startsWith('blob:') || url.startsWith('http')) {
22
+ return url;
23
+ }
24
+ // Handle our custom asset protocol
25
+ if (url.startsWith('asset://')) {
26
+ const id = url.replace('asset://', '');
27
+ // Check cache first
28
+ if (urlCache.has(id)) {
29
+ return urlCache.get(id);
30
+ }
31
+ try {
32
+ const file = await get(`${ASSET_PREFIX}${id}`);
33
+ if (!file) {
34
+ console.warn(`Asset not found: ${id}`);
35
+ return null;
36
+ }
37
+ const objectUrl = URL.createObjectURL(file);
38
+ urlCache.set(id, objectUrl);
39
+ return objectUrl;
40
+ }
41
+ catch (error) {
42
+ console.error('Failed to load asset:', error);
43
+ return null;
44
+ }
45
+ }
46
+ // Legacy data URLs are returned as is
47
+ return url;
48
+ }
@@ -0,0 +1,34 @@
1
+ import type { WallNode } from '../schema';
2
+ export type Space = {
3
+ id: string;
4
+ levelId: string;
5
+ polygon: Array<[number, number]>;
6
+ wallIds: string[];
7
+ isExterior: boolean;
8
+ };
9
+ /**
10
+ * Initializes space detection sync with scene and editor stores
11
+ * Call this once during app initialization
12
+ */
13
+ export declare function initSpaceDetectionSync(sceneStore: any, // useScene store
14
+ editorStore: any): () => void;
15
+ type WallSideUpdate = {
16
+ wallId: string;
17
+ frontSide: 'interior' | 'exterior' | 'unknown';
18
+ backSide: 'interior' | 'exterior' | 'unknown';
19
+ };
20
+ /**
21
+ * Detects spaces for a level by flood-filling a grid from the edges
22
+ * Returns wall side updates and detected spaces
23
+ */
24
+ export declare function detectSpacesForLevel(levelId: string, walls: WallNode[], gridResolution?: number): {
25
+ wallUpdates: WallSideUpdate[];
26
+ spaces: Space[];
27
+ };
28
+ /**
29
+ * Checks if a wall touches any other walls
30
+ * Used to determine if space detection should run
31
+ */
32
+ export declare function wallTouchesOthers(wall: WallNode, otherWalls: WallNode[]): boolean;
33
+ export {};
34
+ //# sourceMappingURL=space-detection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"space-detection.d.ts","sourceRoot":"","sources":["../../src/lib/space-detection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAMzC,MAAM,MAAM,KAAK,GAAG;IAClB,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAChC,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,UAAU,EAAE,OAAO,CAAA;CACpB,CAAA;AAMD;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,GAAG,EAAE,iBAAiB;AAClC,WAAW,EAAE,GAAG,GACf,MAAM,IAAI,CAgGZ;AA+DD,KAAK,cAAc,GAAG;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,UAAU,GAAG,UAAU,GAAG,SAAS,CAAA;IAC9C,QAAQ,EAAE,UAAU,GAAG,UAAU,GAAG,SAAS,CAAA;CAC9C,CAAA;AAMD;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,QAAQ,EAAE,EACjB,cAAc,GAAE,MAAY,GAC3B;IACD,WAAW,EAAE,cAAc,EAAE,CAAA;IAC7B,MAAM,EAAE,KAAK,EAAE,CAAA;CAChB,CAqBA;AAsWD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,OAAO,CAkBjF"}