@pascal-app/core 1.0.0-beta.2 → 1.0.0-beta.4

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/index.d.ts CHANGED
@@ -17,10 +17,10 @@ export { getRenderableSlabPolygon, type SlabEdgeWallBandSnap, type SlabPolygonCo
17
17
  export { deriveSlotId, isSlotMaterialName, SLOT_MATERIAL_PREFIX, slotLabelFromId, } from './lib/slots';
18
18
  export { type AutoCeilingPlanningContext, type AutoCeilingSyncPlan, type AutoSlabPlanningContext, type AutoSlabSyncPlan, type AutoZoneSyncPlan, detectSpacesForLevel, initSpaceDetectionSync, isSpaceDetectionPaused, pauseSpaceDetection, planAutoCeilingsForLevel, planAutoSlabsForLevel, planAutoZonesForLevel, resolveAutoZonePolygon, resumeSpaceDetection, type Space, type SpaceBoundaryFace, wallClosesRoom, wallTouchesOthers, } from './lib/space-detection';
19
19
  export { advanceStroke, type BrushSettings, type BrushShape, beginStroke, brushHeightAt, DEFAULT_BRUSH_SETTINGS, detachStrokeAnchor, highestOver, MIN_BRUSH_RADIUS_IN_SPACINGS, maxCoverage, minBrushRadius, RAISE_METRES_PER_STROKE, sampleTarget, type TerrainStroke, type TerrainVerb, weightAt, } from './lib/terrain-brush';
20
- export { decodeTerrainField, encodeTerrainField, isDatumField } from './lib/terrain-codec';
20
+ export { decodeHeightPatch, decodeTerrainField, type EncodedHeightPatch, encodeHeightPatch, encodeTerrainField, isDatumField, } from './lib/terrain-codec';
21
21
  export { applyHeightPatch, createTerrainField, DEFAULT_TERRAIN_SPACING, DEFAULT_TERRAIN_STEP, diffToPatches, flattenPatch, type HeightPatch, heightAt, heightAtSample, isFlatOver, normalAt, quantize, sampleRangeOver, slopeAt, surfaceHeightAt, type TerrainField, } from './lib/terrain-field';
22
22
  export { raycastTerrain, type TerrainHit } from './lib/terrain-raycast';
23
- export { commitTerrainField, terrainFieldForEdit, terrainFieldOf } from './lib/terrain-source';
23
+ export { commitTerrainField, persistedTerrainFieldOf, terrainFieldForEdit, terrainFieldOf, } from './lib/terrain-source';
24
24
  export { isLevelBaseConsumer, isSiteDatum, levelBaseElevationAt, noteLevelBaseConsumer, SITE_DATUM_EPSILON, SITE_DATUM_Y, terrainSupportLift, } from './lib/terrain-support';
25
25
  export { closestOnSegment, collectLevelWallSegments, nearestWallSegment, WALL_SNAP_DISTANCE_M, type WallSegment, type WallSegmentClosest, } from './lib/wall-distance';
26
26
  export { deriveZoneQuantityReport, type ZoneQuantityReport, type ZoneQuantityValue, } from './lib/zone-quantities';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -16,10 +16,10 @@ export { getRenderableSlabPolygon, slabPolygonContextFromGeometry, snapSlabEdgeT
16
16
  export { deriveSlotId, isSlotMaterialName, SLOT_MATERIAL_PREFIX, slotLabelFromId, } from './lib/slots';
17
17
  export { detectSpacesForLevel, initSpaceDetectionSync, isSpaceDetectionPaused, pauseSpaceDetection, planAutoCeilingsForLevel, planAutoSlabsForLevel, planAutoZonesForLevel, resolveAutoZonePolygon, resumeSpaceDetection, wallClosesRoom, wallTouchesOthers, } from './lib/space-detection';
18
18
  export { advanceStroke, beginStroke, brushHeightAt, DEFAULT_BRUSH_SETTINGS, detachStrokeAnchor, highestOver, MIN_BRUSH_RADIUS_IN_SPACINGS, maxCoverage, minBrushRadius, RAISE_METRES_PER_STROKE, sampleTarget, weightAt, } from './lib/terrain-brush';
19
- export { decodeTerrainField, encodeTerrainField, isDatumField } from './lib/terrain-codec';
19
+ export { decodeHeightPatch, decodeTerrainField, encodeHeightPatch, encodeTerrainField, isDatumField, } from './lib/terrain-codec';
20
20
  export { applyHeightPatch, createTerrainField, DEFAULT_TERRAIN_SPACING, DEFAULT_TERRAIN_STEP, diffToPatches, flattenPatch, heightAt, heightAtSample, isFlatOver, normalAt, quantize, sampleRangeOver, slopeAt, surfaceHeightAt, } from './lib/terrain-field';
21
21
  export { raycastTerrain } from './lib/terrain-raycast';
22
- export { commitTerrainField, terrainFieldForEdit, terrainFieldOf } from './lib/terrain-source';
22
+ export { commitTerrainField, persistedTerrainFieldOf, terrainFieldForEdit, terrainFieldOf, } from './lib/terrain-source';
23
23
  export { isLevelBaseConsumer, isSiteDatum, levelBaseElevationAt, noteLevelBaseConsumer, SITE_DATUM_EPSILON, SITE_DATUM_Y, terrainSupportLift, } from './lib/terrain-support';
24
24
  export { closestOnSegment, collectLevelWallSegments, nearestWallSegment, WALL_SNAP_DISTANCE_M, } from './lib/wall-distance';
25
25
  export { deriveZoneQuantityReport, } from './lib/zone-quantities';
@@ -25,7 +25,12 @@
25
25
  * the scene.
26
26
  */
27
27
  import { type TerrainData } from '../schema/terrain';
28
- import type { TerrainField } from './terrain-field';
28
+ import type { HeightPatch, TerrainField } from './terrain-field';
29
+ export type EncodedHeightPatch = Omit<HeightPatch, 'heights'> & {
30
+ heights: string;
31
+ };
32
+ export declare function encodeHeightPatch(patch: HeightPatch): EncodedHeightPatch;
33
+ export declare function decodeHeightPatch(value: unknown): HeightPatch | null;
29
34
  export declare function encodeTerrainField(field: TerrainField): TerrainData;
30
35
  /**
31
36
  * Rebuild a field from persisted data, or `null` if the data is unusable.
@@ -1 +1 @@
1
- {"version":3,"file":"terrain-codec.d.ts","sourceRoot":"","sources":["../../src/lib/terrain-codec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAoB,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACtE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAoDnD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,GAAG,WAAW,CAenE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,YAAY,GAAG,IAAI,CAwCrE;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAKzD"}
1
+ {"version":3,"file":"terrain-codec.d.ts","sourceRoot":"","sources":["../../src/lib/terrain-codec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAoB,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAyEhE,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAEnF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,WAAW,GAAG,kBAAkB,CAExE;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,GAAG,IAAI,CA4BpE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,GAAG,WAAW,CAUnE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,YAAY,GAAG,IAAI,CAgCrE;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAKzD"}
@@ -76,12 +76,55 @@ function decodeBase64(text) {
76
76
  }
77
77
  return bytes;
78
78
  }
79
- export function encodeTerrainField(field) {
80
- const bytes = new Uint8Array(field.heights.length * 2);
79
+ function encodeInt16Samples(samples) {
80
+ const bytes = new Uint8Array(samples.length * 2);
81
81
  const view = new DataView(bytes.buffer);
82
- for (let i = 0; i < field.heights.length; i++) {
83
- view.setInt16(i * 2, field.heights[i] ?? 0, true);
82
+ for (let i = 0; i < samples.length; i++) {
83
+ view.setInt16(i * 2, samples[i] ?? 0, true);
84
+ }
85
+ return encodeBase64(bytes);
86
+ }
87
+ function decodeInt16Samples(text, sampleCount) {
88
+ const bytes = decodeBase64(text);
89
+ if (!bytes || encodeBase64(bytes) !== text || bytes.byteLength !== sampleCount * 2)
90
+ return null;
91
+ const samples = new Int16Array(sampleCount);
92
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
93
+ for (let i = 0; i < samples.length; i++) {
94
+ samples[i] = view.getInt16(i * 2, true);
84
95
  }
96
+ return samples;
97
+ }
98
+ export function encodeHeightPatch(patch) {
99
+ return { ...patch, heights: encodeInt16Samples(patch.heights) };
100
+ }
101
+ export function decodeHeightPatch(value) {
102
+ if (!value || typeof value !== 'object')
103
+ return null;
104
+ const patch = value;
105
+ if (!Number.isInteger(patch.col0) ||
106
+ !Number.isInteger(patch.row0) ||
107
+ !Number.isInteger(patch.cols) ||
108
+ !Number.isInteger(patch.rows)) {
109
+ return null;
110
+ }
111
+ const col0 = patch.col0;
112
+ const row0 = patch.row0;
113
+ const cols = patch.cols;
114
+ const rows = patch.rows;
115
+ if (col0 < 0 ||
116
+ row0 < 0 ||
117
+ cols < 1 ||
118
+ rows < 1 ||
119
+ col0 + cols > MAX_TERRAIN_SIDE ||
120
+ row0 + rows > MAX_TERRAIN_SIDE ||
121
+ typeof patch.heights !== 'string') {
122
+ return null;
123
+ }
124
+ const heights = decodeInt16Samples(patch.heights, cols * rows);
125
+ return heights ? { col0, row0, cols, rows, heights } : null;
126
+ }
127
+ export function encodeTerrainField(field) {
85
128
  return {
86
129
  type: 'heightfield',
87
130
  origin: [field.origin[0], field.origin[1]],
@@ -89,7 +132,7 @@ export function encodeTerrainField(field) {
89
132
  cols: field.cols,
90
133
  rows: field.rows,
91
134
  step: field.step,
92
- heights: encodeBase64(bytes),
135
+ heights: encodeInt16Samples(field.heights),
93
136
  };
94
137
  }
95
138
  /**
@@ -130,18 +173,9 @@ export function decodeTerrainField(data) {
130
173
  !Number.isFinite(d.origin[1])) {
131
174
  return null;
132
175
  }
133
- const bytes = decodeBase64(d.heights);
134
- if (!bytes)
176
+ const heights = decodeInt16Samples(d.heights, cols * rows);
177
+ if (!heights)
135
178
  return null;
136
- if (encodeBase64(bytes) !== d.heights)
137
- return null;
138
- if (bytes.byteLength !== cols * rows * 2)
139
- return null;
140
- const heights = new Int16Array(cols * rows);
141
- const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
142
- for (let i = 0; i < heights.length; i++) {
143
- heights[i] = view.getInt16(i * 2, true);
144
- }
145
179
  return {
146
180
  origin: [d.origin[0], d.origin[1]],
147
181
  spacing: d.spacing,
@@ -39,6 +39,7 @@ import { createTerrainField, type TerrainField } from './terrain-field';
39
39
  export declare function terrainFieldOf(site: (Pick<SiteNode, 'terrain'> & {
40
40
  id?: string;
41
41
  }) | null | undefined): TerrainField | null;
42
+ export declare function persistedTerrainFieldOf(site: Pick<SiteNode, 'terrain'> | null | undefined): TerrainField | null;
42
43
  /**
43
44
  * The field a site *would* sculpt into: its existing terrain, or a fresh flat
44
45
  * field sized to `options`.
@@ -1 +1 @@
1
- {"version":3,"file":"terrain-source.d.ts","sourceRoot":"","sources":["../../src/lib/terrain-source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAGpD,OAAO,EAAE,kBAAkB,EAAE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAUvE;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,IAAI,GAAG,SAAS,GACrE,YAAY,GAAG,IAAI,CAkBrB;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,GAAG,IAAI,GAAG,SAAS,EAClD,OAAO,CAAC,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC,CAAC,CAAC,GACjD,YAAY,CAEd;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,GAAG,WAAW,CAInE"}
1
+ {"version":3,"file":"terrain-source.d.ts","sourceRoot":"","sources":["../../src/lib/terrain-source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAGpD,OAAO,EAAE,kBAAkB,EAAE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAUvE;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,IAAI,GAAG,SAAS,GACrE,YAAY,GAAG,IAAI,CAMrB;AAED,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,GAAG,IAAI,GAAG,SAAS,GACjD,YAAY,GAAG,IAAI,CAcrB;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,GAAG,IAAI,GAAG,SAAS,EAClD,OAAO,CAAC,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC,CAAC,CAAC,GACjD,YAAY,CAEd;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,GAAG,WAAW,CAInE"}
@@ -48,6 +48,9 @@ export function terrainFieldOf(site) {
48
48
  if (live)
49
49
  return live;
50
50
  }
51
+ return persistedTerrainFieldOf(site);
52
+ }
53
+ export function persistedTerrainFieldOf(site) {
51
54
  const data = site?.terrain;
52
55
  if (!data)
53
56
  return null;
@@ -73,7 +76,7 @@ export function terrainFieldOf(site) {
73
76
  * would force every reader to allocate a field it does not need.
74
77
  */
75
78
  export function terrainFieldForEdit(site, options) {
76
- return terrainFieldOf(site) ?? createTerrainField(options);
79
+ return persistedTerrainFieldOf(site) ?? createTerrainField(options);
77
80
  }
78
81
  /**
79
82
  * Encode a field for persistence and prime the read cache with it.
@@ -31,9 +31,15 @@ export type LiveTerrainStroke = {
31
31
  */
32
32
  readonly lastPatch: HeightPatch | null;
33
33
  };
34
+ export type RemoteLiveTerrainStroke = {
35
+ readonly sourceId: string;
36
+ readonly field: TerrainField;
37
+ readonly lastPatch: HeightPatch;
38
+ };
34
39
  type LiveTerrainState = {
35
40
  /** Keyed by site node id — a scene can hold more than one site. */
36
41
  strokes: Map<string, LiveTerrainStroke>;
42
+ remoteStrokes: Map<string, RemoteLiveTerrainStroke>;
37
43
  /**
38
44
  * Start a stroke from `field`.
39
45
  *
@@ -56,6 +62,10 @@ type LiveTerrainState = {
56
62
  /** The live field for a site, or undefined when no stroke is in flight. */
57
63
  fieldOf(siteId: string): TerrainField | undefined;
58
64
  strokeOf(siteId: string): LiveTerrainStroke | undefined;
65
+ remoteStrokeOf(siteId: string): RemoteLiveTerrainStroke | undefined;
66
+ previewRemote(siteId: string, sourceId: string, field: TerrainField, patch: HeightPatch): void;
67
+ endRemote(siteId: string, sourceId: string): void;
68
+ endRemoteSource(sourceId: string): void;
59
69
  /** End the stroke. The caller persists the field first if it wants to keep it. */
60
70
  end(siteId: string): void;
61
71
  endAll(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"use-live-terrain.d.ts","sourceRoot":"","sources":["../../src/store/use-live-terrain.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAErE;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,4EAA4E;IAC5E,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAA;IAC/B,+EAA+E;IAC/E,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAA;IAC5B;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,WAAW,GAAG,IAAI,CAAA;CACvC,CAAA;AAED,KAAK,gBAAgB,GAAG;IACtB,mEAAmE;IACnE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAA;IACvC;;;;;;;;;;;;;;;OAeG;IACH,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI,CAAA;IAChD,oEAAoE;IACpE,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,CAAA;IACtE,2EAA2E;IAC3E,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAAA;IACjD,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAAA;IACvD,kFAAkF;IAClF,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,MAAM,IAAI,IAAI,CAAA;CACf,CAAA;AAED,QAAA,MAAM,cAAc,+EAkCjB,CAAA;AAEH,eAAe,cAAc,CAAA"}
1
+ {"version":3,"file":"use-live-terrain.d.ts","sourceRoot":"","sources":["../../src/store/use-live-terrain.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAErE;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,4EAA4E;IAC5E,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAA;IAC/B,+EAA+E;IAC/E,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAA;IAC5B;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,WAAW,GAAG,IAAI,CAAA;CACvC,CAAA;AAED,MAAM,MAAM,uBAAuB,GAAG;IACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAA;IAC5B,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAA;CAChC,CAAA;AAED,KAAK,gBAAgB,GAAG;IACtB,mEAAmE;IACnE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAA;IACvC,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAA;IACnD;;;;;;;;;;;;;;;OAeG;IACH,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI,CAAA;IAChD,oEAAoE;IACpE,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,CAAA;IACtE,2EAA2E;IAC3E,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAAA;IACjD,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAAA;IACvD,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,uBAAuB,GAAG,SAAS,CAAA;IACnE,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,CAAA;IAC9F,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACjD,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvC,kFAAkF;IAClF,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,MAAM,IAAI,IAAI,CAAA;CACf,CAAA;AAED,QAAA,MAAM,cAAc,+EAiEjB,CAAA;AAEH,eAAe,cAAc,CAAA"}
@@ -1,6 +1,7 @@
1
1
  import { create } from 'zustand';
2
2
  const useLiveTerrain = create((set, get) => ({
3
3
  strokes: new Map(),
4
+ remoteStrokes: new Map(),
4
5
  begin: (siteId, field) => set((state) => {
5
6
  const next = new Map(state.strokes);
6
7
  next.set(siteId, { snapshot: field, field, lastPatch: null });
@@ -17,8 +18,29 @@ const useLiveTerrain = create((set, get) => ({
17
18
  next.set(siteId, { snapshot: current.snapshot, field, lastPatch: patch });
18
19
  return { strokes: next };
19
20
  }),
20
- fieldOf: (siteId) => get().strokes.get(siteId)?.field,
21
+ fieldOf: (siteId) => get().strokes.get(siteId)?.field ?? get().remoteStrokes.get(siteId)?.field,
21
22
  strokeOf: (siteId) => get().strokes.get(siteId),
23
+ remoteStrokeOf: (siteId) => get().remoteStrokes.get(siteId),
24
+ previewRemote: (siteId, sourceId, field, patch) => set((state) => {
25
+ const next = new Map(state.remoteStrokes);
26
+ next.set(siteId, { sourceId, field, lastPatch: patch });
27
+ return { remoteStrokes: next };
28
+ }),
29
+ endRemote: (siteId, sourceId) => set((state) => {
30
+ if (state.remoteStrokes.get(siteId)?.sourceId !== sourceId)
31
+ return state;
32
+ const next = new Map(state.remoteStrokes);
33
+ next.delete(siteId);
34
+ return { remoteStrokes: next };
35
+ }),
36
+ endRemoteSource: (sourceId) => set((state) => {
37
+ const next = new Map(state.remoteStrokes);
38
+ for (const [siteId, stroke] of next) {
39
+ if (stroke.sourceId === sourceId)
40
+ next.delete(siteId);
41
+ }
42
+ return next.size === state.remoteStrokes.size ? state : { remoteStrokes: next };
43
+ }),
22
44
  end: (siteId) => set((state) => {
23
45
  if (!state.strokes.has(siteId))
24
46
  return state;
@@ -26,6 +48,8 @@ const useLiveTerrain = create((set, get) => ({
26
48
  next.delete(siteId);
27
49
  return { strokes: next };
28
50
  }),
29
- endAll: () => set((state) => (state.strokes.size === 0 ? state : { strokes: new Map() })),
51
+ endAll: () => set((state) => state.strokes.size === 0 && state.remoteStrokes.size === 0
52
+ ? state
53
+ : { remoteStrokes: new Map(), strokes: new Map() }),
30
54
  }));
31
55
  export default useLiveTerrain;
@@ -1 +1 @@
1
- {"version":3,"file":"use-scene.d.ts","sourceRoot":"","sources":["../../src/store/use-scene.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"use-scene.d.ts","sourceRoot":"","sources":["../../src/store/use-scene.ts"],"names":[],"mappings":"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"}
@@ -23,6 +23,7 @@ import { getCeilingClampBound } from '../services/storey';
23
23
  import { computeWallSlabSupport } from '../systems/slab/slab-support';
24
24
  import { DEFAULT_WALL_HEIGHT } from '../systems/wall/wall-footprint';
25
25
  import { healSceneNodes } from '../utils/heal-scene-graph';
26
+ import { removeRetiredDrawingSheetNodes } from '../utils/retired-scene-nodes';
26
27
  import * as nodeActions from './actions/node-actions';
27
28
  import { areSceneSnapshotsEqual, getSceneHistoryPauseDepth, notifySceneCommit, pauseSceneHistory, resetSceneHistoryPauseDepth, resumeSceneHistory, } from './history-control';
28
29
  import useLiveNodeOverrides from './use-live-node-overrides';
@@ -468,26 +469,6 @@ function migrateConstructionDimension(node) {
468
469
  }),
469
470
  };
470
471
  }
471
- function removeRetiredDrawingSheets(nodes) {
472
- const retiredIds = new Set(Object.entries(nodes)
473
- .filter(([, node]) => node?.type === 'drawing-sheet')
474
- .map(([id]) => id));
475
- if (retiredIds.size === 0)
476
- return;
477
- for (const id of retiredIds)
478
- delete nodes[id];
479
- for (const [id, node] of Object.entries(nodes)) {
480
- if (!Array.isArray(node?.children))
481
- continue;
482
- const children = getStringArray(node.children);
483
- if (!children.some((childId) => retiredIds.has(childId)))
484
- continue;
485
- nodes[id] = {
486
- ...node,
487
- children: children.filter((childId) => !retiredIds.has(childId)),
488
- };
489
- }
490
- }
491
472
  function migrateWallAssembly(node) {
492
473
  if (!Object.hasOwn(node, 'assemblyLayers'))
493
474
  return node;
@@ -516,8 +497,7 @@ function migrateNodes(nodes) {
516
497
  // Repair pre-existing corruption (null children, zero-length walls) before
517
498
  // any per-type migration runs, so already-saved scenes load cleanly.
518
499
  const { nodes: healed } = healSceneNodes(nodes);
519
- const patchedNodes = { ...healed };
520
- removeRetiredDrawingSheets(patchedNodes);
500
+ const { nodes: patchedNodes } = removeRetiredDrawingSheetNodes(healed);
521
501
  // Scene materials minted while moving legacy wall fields onto `node.slots`;
522
502
  // merged into the scene material map by the caller (`setScene`).
523
503
  const mintedMaterials = {};
@@ -1100,22 +1080,24 @@ const useScene = create()(temporal((set, get) => ({
1100
1080
  });
1101
1081
  return; // Scene already loaded
1102
1082
  }
1103
- // Create hierarchy: Site → Building → Level
1083
+ // Create hierarchy: Site → Building → Level. Parent links must be
1084
+ // written explicitly — the schema defaults `parentId` to null, and the
1085
+ // scene authority rejects parent/child asymmetry that the renderer
1086
+ // happily traverses through `children`.
1087
+ const site = SiteNode.parse({});
1088
+ const building = BuildingNode.parse({
1089
+ parentId: site.id,
1090
+ });
1104
1091
  const level0 = LevelNode.parse({
1092
+ parentId: building.id,
1105
1093
  level: 0,
1106
1094
  children: [],
1107
1095
  height: 2.5,
1108
1096
  });
1109
- const building = BuildingNode.parse({
1110
- children: [level0.id],
1111
- });
1112
- const site = SiteNode.parse({
1113
- children: [building.id],
1114
- });
1115
1097
  // Define all nodes flat
1116
1098
  const nodes = {
1117
- [site.id]: site,
1118
- [building.id]: building,
1099
+ [site.id]: { ...site, children: [building.id] },
1100
+ [building.id]: { ...building, children: [level0.id] },
1119
1101
  [level0.id]: level0,
1120
1102
  };
1121
1103
  // Site is the root
@@ -9,6 +9,11 @@ export interface HealSceneResult {
9
9
  * a different node (stale reparent leftovers), plus same-array duplicates.
10
10
  */
11
11
  strippedStaleChildRefs: number;
12
+ /**
13
+ * Ids of nodes whose null `parentId` was repaired to the one parent that
14
+ * still claims them via `children`.
15
+ */
16
+ repairedParentLinkNodeIds: string[];
12
17
  }
13
18
  /**
14
19
  * Returns a healed copy of a `nodes` map. Pure — does not mutate `input`.
@@ -1 +1 @@
1
- {"version":3,"file":"heal-scene-graph.d.ts","sourceRoot":"","sources":["../../src/utils/heal-scene-graph.ts"],"names":[],"mappings":"AAqBA,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,kDAAkD;IAClD,cAAc,EAAE,MAAM,EAAE,CAAA;IACxB,sFAAsF;IACtF,iBAAiB,EAAE,MAAM,CAAA;IACzB;;;OAGG;IACH,sBAAsB,EAAE,MAAM,CAAA;CAC/B;AAgBD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,eAAe,CA2E9E"}
1
+ {"version":3,"file":"heal-scene-graph.d.ts","sourceRoot":"","sources":["../../src/utils/heal-scene-graph.ts"],"names":[],"mappings":"AA0BA,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,kDAAkD;IAClD,cAAc,EAAE,MAAM,EAAE,CAAA;IACxB,sFAAsF;IACtF,iBAAiB,EAAE,MAAM,CAAA;IACzB;;;OAGG;IACH,sBAAsB,EAAE,MAAM,CAAA;IAC9B;;;OAGG;IACH,yBAAyB,EAAE,MAAM,EAAE,CAAA;CACpC;AAgBD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,eAAe,CAiH9E"}
@@ -13,6 +13,11 @@
13
13
  // duplicate reference renders the child twice (duplicate React keys in the
14
14
  // 2D plan, doubled hosted geometry in 3D). Same-array duplicates are
15
15
  // collapsed too.
16
+ // 4. A node with a null `parentId` that exactly one parent still claims via
17
+ // `children` (the legacy site-child flatten and the old default-scene
18
+ // assembler both linked children without writing `parentId`). The editor
19
+ // renders the chain through `children`, but the hosted scene authority
20
+ // validates parent/child symmetry and rejects the whole scene.
16
21
  //
17
22
  // All are also prevented at the source now; this is the load-time safety net
18
23
  // for already-saved scenes.
@@ -102,5 +107,45 @@ export function healSceneNodes(input) {
102
107
  }
103
108
  nodes[id] = node;
104
109
  }
105
- return { nodes, droppedWallIds, strippedChildRefs, strippedStaleChildRefs };
110
+ // Pass 3: repair null parent links. A node claimed as a child by exactly one
111
+ // parent must point back at it; legacy writers linked `children` without
112
+ // writing `parentId`. Embedded legacy site children claim by their `id` so
113
+ // the flattened flat-map node is repaired too.
114
+ const claimantsByChildId = new Map();
115
+ for (const [id, node] of Object.entries(nodes)) {
116
+ const children = node?.children;
117
+ if (!Array.isArray(children))
118
+ continue;
119
+ for (const child of children) {
120
+ const childId = typeof child === 'string'
121
+ ? child
122
+ : child && typeof child === 'object' && typeof child.id === 'string'
123
+ ? child.id
124
+ : null;
125
+ if (!childId || !(childId in nodes))
126
+ continue;
127
+ const claimants = claimantsByChildId.get(childId) ?? [];
128
+ claimants.push(id);
129
+ claimantsByChildId.set(childId, claimants);
130
+ }
131
+ }
132
+ const repairedParentLinkNodeIds = [];
133
+ for (const [id, node] of Object.entries(nodes)) {
134
+ if (!node || typeof node !== 'object')
135
+ continue;
136
+ if (node.parentId != null)
137
+ continue;
138
+ const claimants = claimantsByChildId.get(id);
139
+ if (claimants?.length !== 1 || claimants[0] === id)
140
+ continue;
141
+ nodes[id] = { ...node, parentId: claimants[0] };
142
+ repairedParentLinkNodeIds.push(id);
143
+ }
144
+ return {
145
+ nodes,
146
+ droppedWallIds,
147
+ strippedChildRefs,
148
+ strippedStaleChildRefs,
149
+ repairedParentLinkNodeIds,
150
+ };
106
151
  }
@@ -0,0 +1,6 @@
1
+ export type RetiredSceneNodeMigration<TNode> = {
2
+ nodes: Record<string, TNode>;
3
+ removedNodeIds: ReadonlySet<string>;
4
+ };
5
+ export declare function removeRetiredDrawingSheetNodes<TNode>(nodes: Record<string, TNode>): RetiredSceneNodeMigration<TNode>;
6
+ //# sourceMappingURL=retired-scene-nodes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retired-scene-nodes.d.ts","sourceRoot":"","sources":["../../src/utils/retired-scene-nodes.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,yBAAyB,CAAC,KAAK,IAAI;IAC7C,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;IAC5B,cAAc,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CACpC,CAAA;AAED,wBAAgB,8BAA8B,CAAC,KAAK,EAClD,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GAC3B,yBAAyB,CAAC,KAAK,CAAC,CAkClC"}
@@ -0,0 +1,30 @@
1
+ export function removeRetiredDrawingSheetNodes(nodes) {
2
+ const removedNodeIds = new Set(Object.entries(nodes)
3
+ .filter(([, node]) => {
4
+ return (node !== null &&
5
+ typeof node === 'object' &&
6
+ !Array.isArray(node) &&
7
+ node.type === 'drawing-sheet');
8
+ })
9
+ .map(([id]) => id));
10
+ if (removedNodeIds.size === 0)
11
+ return { nodes, removedNodeIds };
12
+ const migratedNodes = { ...nodes };
13
+ for (const id of removedNodeIds)
14
+ delete migratedNodes[id];
15
+ for (const [id, node] of Object.entries(migratedNodes)) {
16
+ if (!(node !== null && typeof node === 'object' && !Array.isArray(node)))
17
+ continue;
18
+ const children = node.children;
19
+ if (!Array.isArray(children))
20
+ continue;
21
+ if (!children.some((childId) => typeof childId === 'string' && removedNodeIds.has(childId))) {
22
+ continue;
23
+ }
24
+ migratedNodes[id] = {
25
+ ...node,
26
+ children: children.filter((childId) => !(typeof childId === 'string' && removedNodeIds.has(childId))),
27
+ };
28
+ }
29
+ return { nodes: migratedNodes, removedNodeIds };
30
+ }
@@ -0,0 +1,3 @@
1
+ export { type HealSceneResult, healSceneNodes } from './heal-scene-graph';
2
+ export { type RetiredSceneNodeMigration, removeRetiredDrawingSheetNodes, } from './retired-scene-nodes';
3
+ //# sourceMappingURL=scene-migrations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene-migrations.d.ts","sourceRoot":"","sources":["../../src/utils/scene-migrations.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,eAAe,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACzE,OAAO,EACL,KAAK,yBAAyB,EAC9B,8BAA8B,GAC/B,MAAM,uBAAuB,CAAA"}
@@ -0,0 +1,6 @@
1
+ // Server-safe scene migrations shared by the client loader and the hosted
2
+ // scene authority. Everything exported here must stay pure data logic with no
3
+ // store, React, or Three.js imports so it can run in Server Components and
4
+ // API routes.
5
+ export { healSceneNodes } from './heal-scene-graph';
6
+ export { removeRetiredDrawingSheetNodes, } from './retired-scene-nodes';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pascal-app/core",
3
- "version": "1.0.0-beta.2",
3
+ "version": "1.0.0-beta.4",
4
4
  "description": "Core library for Pascal 3D building editor",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -16,6 +16,11 @@
16
16
  "import": "./dist/utils/clone-scene-graph.js",
17
17
  "default": "./dist/utils/clone-scene-graph.js"
18
18
  },
19
+ "./scene-migrations": {
20
+ "types": "./dist/utils/scene-migrations.d.ts",
21
+ "import": "./dist/utils/scene-migrations.js",
22
+ "default": "./dist/utils/scene-migrations.js"
23
+ },
19
24
  "./registry": {
20
25
  "types": "./dist/registry/index.d.ts",
21
26
  "import": "./dist/registry/index.js",