@pascal-app/viewer 1.0.0 → 1.0.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.
- package/README.md +12 -0
- package/dist/capture/capture-runtime.js +2 -32
- package/dist/capture/layers/device-motion-layer.d.ts.map +1 -1
- package/dist/capture/layers/device-motion-layer.js +5 -7
- package/dist/capture/stream-rendering.d.ts.map +1 -1
- package/dist/capture/stream-rendering.js +2 -1
- package/dist/capture/use-json-artifact.d.ts +2 -0
- package/dist/capture/use-json-artifact.d.ts.map +1 -0
- package/dist/capture/use-json-artifact.js +42 -0
- package/dist/components/viewer/glb-scene.d.ts.map +1 -1
- package/dist/components/viewer/glb-scene.js +3 -1
- package/dist/components/viewer/index.d.ts +12 -0
- package/dist/components/viewer/index.d.ts.map +1 -1
- package/dist/components/viewer/index.js +28 -7
- package/dist/components/viewer/post-processing.d.ts.map +1 -1
- package/dist/components/viewer/post-processing.js +6 -0
- package/dist/components/viewer/selection-manager.d.ts.map +1 -1
- package/dist/components/viewer/selection-manager.js +8 -16
- package/dist/components/viewer/viewer-camera.d.ts +15 -1
- package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
- package/dist/components/viewer/viewer-camera.js +43 -5
- package/dist/index.d.ts +8 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -6
- package/dist/lib/csg-utils.d.ts +1 -1
- package/dist/lib/csg-utils.d.ts.map +1 -1
- package/dist/lib/csg-utils.js +1 -1
- package/dist/lib/isolation.d.ts +14 -30
- package/dist/lib/isolation.d.ts.map +1 -1
- package/dist/lib/isolation.js +62 -66
- package/dist/lib/materials.d.ts.map +1 -1
- package/dist/lib/materials.js +2 -1
- package/dist/lib/renderer-capability.d.ts +2 -1
- package/dist/lib/renderer-capability.d.ts.map +1 -1
- package/dist/lib/renderer-capability.js +2 -2
- package/dist/lib/scene-visibility.js +3 -1
- package/dist/lib/snapshot-pipeline.d.ts +3 -1
- package/dist/lib/snapshot-pipeline.d.ts.map +1 -1
- package/dist/lib/snapshot-pipeline.js +32 -16
- package/dist/store/use-viewer.d.ts +3 -1
- package/dist/store/use-viewer.d.ts.map +1 -1
- package/dist/store/use-viewer.js +2 -0
- package/dist/systems/door/door-system.d.ts.map +1 -1
- package/dist/systems/door/door-system.js +18 -5
- package/dist/systems/floor-elevation/floor-elevation-system.d.ts.map +1 -1
- package/dist/systems/floor-elevation/floor-elevation-system.js +122 -3
- package/dist/systems/geometry/geometry-system.d.ts.map +1 -1
- package/dist/systems/geometry/geometry-system.js +4 -0
- package/dist/systems/level/level-system.js +14 -14
- package/dist/systems/level/level-utils.d.ts +25 -1
- package/dist/systems/level/level-utils.d.ts.map +1 -1
- package/dist/systems/level/level-utils.js +25 -4
- package/dist/systems/wall/wall-cutout-cache.d.ts +4 -1
- package/dist/systems/wall/wall-cutout-cache.d.ts.map +1 -1
- package/dist/systems/wall/wall-cutout-cache.js +16 -7
- package/dist/systems/wall/wall-cutout.d.ts +3 -1
- package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
- package/dist/systems/wall/wall-cutout.js +5 -5
- package/dist/systems/wall/wall-materials.d.ts +11 -2
- package/dist/systems/wall/wall-materials.d.ts.map +1 -1
- package/dist/systems/wall/wall-materials.js +24 -18
- package/dist/systems/wall/wall-system.d.ts +15 -2
- package/dist/systems/wall/wall-system.d.ts.map +1 -1
- package/dist/systems/wall/wall-system.js +20 -21
- package/dist/systems/window/window-system.d.ts +1 -1
- package/dist/systems/window/window-system.d.ts.map +1 -1
- package/dist/systems/window/window-system.js +3 -2
- package/dist/systems/zone/zone-system.d.ts.map +1 -1
- package/dist/systems/zone/zone-system.js +28 -2
- package/dist/xr/presentation-context.d.ts +7 -0
- package/dist/xr/presentation-context.d.ts.map +1 -0
- package/dist/xr/presentation-context.js +10 -0
- package/package.json +4 -4
|
@@ -11,8 +11,21 @@ export declare function mergeWallCutoutBrushes(brushes: readonly Brush[]): {
|
|
|
11
11
|
export declare function shouldDeferWallRebuild(wallId: string, nodes: Record<AnyNodeId, AnyNode>, rebuiltThisFrame: number, elapsedMs: number): boolean;
|
|
12
12
|
/** Rebuilds this system still owes — neighbours deferred during a drag. */
|
|
13
13
|
export declare function getPendingWallRebuildCount(): number;
|
|
14
|
-
export
|
|
15
|
-
|
|
14
|
+
export type WallGeometryAdapterContext = {
|
|
15
|
+
isLive: (id: AnyNodeId) => boolean;
|
|
16
|
+
};
|
|
17
|
+
export type WallGeometryAdapter = {
|
|
18
|
+
prepareChildren?: (wall: WallNode, children: readonly AnyNode[], context: WallGeometryAdapterContext) => {
|
|
19
|
+
envelopeChildren: AnyNode[];
|
|
20
|
+
renderChildren: AnyNode[];
|
|
21
|
+
};
|
|
22
|
+
buildGeometry?: (wall: WallNode, envelope: THREE.BufferGeometry, children: readonly AnyNode[]) => THREE.BufferGeometry;
|
|
23
|
+
syncAuxiliaryGeometry?: (wall: WallNode, mesh: THREE.Mesh, geometry: THREE.BufferGeometry) => void;
|
|
24
|
+
};
|
|
25
|
+
export declare const WallSystem: ({ geometryAdapter }?: {
|
|
26
|
+
geometryAdapter?: WallGeometryAdapter;
|
|
27
|
+
}) => null;
|
|
28
|
+
export declare function runWallBuildFrame(geometryAdapter?: WallGeometryAdapter): void;
|
|
16
29
|
type WallTerrainBottomSampler = (x: number, z: number) => number | null;
|
|
17
30
|
export declare function generateExtrudedWall(wallNode: WallNode, childrenNodes: AnyNode[], miterData: WallMiterData, slabElevation?: number, baseElevation?: number, baseSegments?: readonly WallSlabSupportSegment[], storeyHeight?: number, terrainBottomAt?: WallTerrainBottomSampler): THREE.BufferGeometry;
|
|
18
31
|
//# sourceMappingURL=wall-system.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wall-system.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-system.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,SAAS,EAyBd,KAAK,aAAa,EAClB,KAAK,QAAQ,EACb,KAAK,sBAAsB,EAI5B,MAAM,kBAAkB,CAAA;AAGzB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAY,KAAK,EAA0B,MAAM,eAAe,CAAA;AAsBvE,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAA;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAA;AA2DhE,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,SAAS,KAAK,EAAE,GAAG;IACjE,MAAM,EAAE,KAAK,GAAG,IAAI,CAAA;IACpB,eAAe,EAAE,KAAK,EAAE,CAAA;IACxB,YAAY,EAAE,MAAM,CAAA;CACrB,CA4FA;AAmbD,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,EACjC,gBAAgB,EAAE,MAAM,EACxB,SAAS,EAAE,MAAM,GAChB,OAAO,CAET;AAED,2EAA2E;AAC3E,wBAAgB,0BAA0B,IAAI,MAAM,CAEnD;AAID,eAAO,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"wall-system.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-system.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,SAAS,EAyBd,KAAK,aAAa,EAClB,KAAK,QAAQ,EACb,KAAK,sBAAsB,EAI5B,MAAM,kBAAkB,CAAA;AAGzB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAY,KAAK,EAA0B,MAAM,eAAe,CAAA;AAsBvE,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAA;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAA;AA2DhE,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,SAAS,KAAK,EAAE,GAAG;IACjE,MAAM,EAAE,KAAK,GAAG,IAAI,CAAA;IACpB,eAAe,EAAE,KAAK,EAAE,CAAA;IACxB,YAAY,EAAE,MAAM,CAAA;CACrB,CA4FA;AAmbD,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,EACjC,gBAAgB,EAAE,MAAM,EACxB,SAAS,EAAE,MAAM,GAChB,OAAO,CAET;AAED,2EAA2E;AAC3E,wBAAgB,0BAA0B,IAAI,MAAM,CAEnD;AAID,MAAM,MAAM,0BAA0B,GAAG;IACvC,MAAM,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,OAAO,CAAA;CACnC,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,eAAe,CAAC,EAAE,CAChB,IAAI,EAAE,QAAQ,EACd,QAAQ,EAAE,SAAS,OAAO,EAAE,EAC5B,OAAO,EAAE,0BAA0B,KAChC;QAAE,gBAAgB,EAAE,OAAO,EAAE,CAAC;QAAC,cAAc,EAAE,OAAO,EAAE,CAAA;KAAE,CAAA;IAC/D,aAAa,CAAC,EAAE,CACd,IAAI,EAAE,QAAQ,EACd,QAAQ,EAAE,KAAK,CAAC,cAAc,EAC9B,QAAQ,EAAE,SAAS,OAAO,EAAE,KACzB,KAAK,CAAC,cAAc,CAAA;IACzB,qBAAqB,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,cAAc,KAAK,IAAI,CAAA;CACnG,CAAA;AAED,eAAO,MAAM,UAAU,GAAI,sBAAqB;IAAE,eAAe,CAAC,EAAE,mBAAmB,CAAA;CAAO,SAM7F,CAAA;AAED,wBAAgB,iBAAiB,CAAC,eAAe,CAAC,EAAE,mBAAmB,QAatE;AAmcD,KAAK,wBAAwB,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAA;AAgEvE,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,QAAQ,EAClB,aAAa,EAAE,OAAO,EAAE,EACxB,SAAS,EAAE,aAAa,EACxB,aAAa,SAAI,EACjB,aAAa,SAAgB,EAC7B,YAAY,GAAE,SAAS,sBAAsB,EAE5C,EACD,YAAY,SAAuB,EACnC,eAAe,CAAC,EAAE,wBAAwB,GACzC,KAAK,CAAC,cAAc,CAwPtB"}
|
|
@@ -496,14 +496,14 @@ export function getPendingWallRebuildCount() {
|
|
|
496
496
|
return drainStats.pendingNeighbours;
|
|
497
497
|
}
|
|
498
498
|
let placeholderSweepCountdown = WALL_PLACEHOLDER_SWEEP_INTERVAL;
|
|
499
|
-
export const WallSystem = () => {
|
|
499
|
+
export const WallSystem = ({ geometryAdapter } = {}) => {
|
|
500
500
|
useScene((state) => state.dirtyNodes);
|
|
501
501
|
useLiveNodeOverrides((s) => s.overrides);
|
|
502
502
|
useEffect(() => () => clearLevelMiterCache(), []);
|
|
503
|
-
useFrame(runWallBuildFrame, 4);
|
|
503
|
+
useFrame(() => runWallBuildFrame(geometryAdapter), 4);
|
|
504
504
|
return null;
|
|
505
505
|
};
|
|
506
|
-
export function runWallBuildFrame() {
|
|
506
|
+
export function runWallBuildFrame(geometryAdapter) {
|
|
507
507
|
const initialBuild = isWallInitialBuildActive();
|
|
508
508
|
const token = useScene.getState().hydrationToken;
|
|
509
509
|
if (token !== stalledHydrationToken) {
|
|
@@ -512,13 +512,13 @@ export function runWallBuildFrame() {
|
|
|
512
512
|
}
|
|
513
513
|
drainStats.wallsConsumedThisFrame = 0;
|
|
514
514
|
try {
|
|
515
|
-
consumeWallBuildFrame(initialBuild);
|
|
515
|
+
consumeWallBuildFrame(initialBuild, geometryAdapter);
|
|
516
516
|
}
|
|
517
517
|
finally {
|
|
518
518
|
publishWallDrainStats();
|
|
519
519
|
}
|
|
520
520
|
}
|
|
521
|
-
function consumeWallBuildFrame(initialBuild) {
|
|
521
|
+
function consumeWallBuildFrame(initialBuild, geometryAdapter) {
|
|
522
522
|
const clearDirty = useScene.getState().clearDirty;
|
|
523
523
|
// Self-heal: any registered wall still on its mount-time placeholder
|
|
524
524
|
// geometry with NO dirty mark gets re-marked, so a lost mark (system
|
|
@@ -600,7 +600,7 @@ function consumeWallBuildFrame(initialBuild) {
|
|
|
600
600
|
}
|
|
601
601
|
const mesh = sceneRegistry.nodes.get(wallId);
|
|
602
602
|
if (mesh) {
|
|
603
|
-
timeSpan('wall-rebuild', () => updateWallGeometry(wallId, miterData), {
|
|
603
|
+
timeSpan('wall-rebuild', () => updateWallGeometry(wallId, miterData, geometryAdapter), {
|
|
604
604
|
properties: [['node', wallId]],
|
|
605
605
|
});
|
|
606
606
|
clearDirty(wallId);
|
|
@@ -677,7 +677,7 @@ function consumeWallBuildFrame(initialBuild) {
|
|
|
677
677
|
}
|
|
678
678
|
const mesh = sceneRegistry.nodes.get(wallId);
|
|
679
679
|
if (mesh) {
|
|
680
|
-
timeSpan('wall-rebuild', () => updateWallGeometry(wallId, miterData), {
|
|
680
|
+
timeSpan('wall-rebuild', () => updateWallGeometry(wallId, miterData, geometryAdapter), {
|
|
681
681
|
properties: [['node', wallId]],
|
|
682
682
|
});
|
|
683
683
|
notifyWallRebuilt(wallId);
|
|
@@ -766,7 +766,7 @@ function getLevelWalls(levelId) {
|
|
|
766
766
|
* (override-merged) so a 2D drag visibly moves the 3D mesh without
|
|
767
767
|
* having touched `useScene` mid-drag.
|
|
768
768
|
*/
|
|
769
|
-
function updateWallGeometry(wallId, miterData) {
|
|
769
|
+
function updateWallGeometry(wallId, miterData, geometryAdapter) {
|
|
770
770
|
const nodes = useScene.getState().nodes;
|
|
771
771
|
const sceneNode = nodes[wallId];
|
|
772
772
|
if (sceneNode?.type !== 'wall')
|
|
@@ -785,38 +785,37 @@ function updateWallGeometry(wallId, miterData) {
|
|
|
785
785
|
? (x, z) => terrainSupportLift(nodes, levelId, x, z)
|
|
786
786
|
: undefined;
|
|
787
787
|
const childrenIds = node.children || [];
|
|
788
|
-
// Merge live overrides into door / window children so cutouts track an
|
|
789
|
-
// in-flight resize drag (door width arrow, window height arrow, etc.)
|
|
790
|
-
// without waiting on the scene store. Non-cutout children pass through
|
|
791
|
-
// unchanged.
|
|
792
788
|
const childrenNodes = childrenIds
|
|
793
789
|
.map((childId) => nodes[childId])
|
|
794
790
|
.filter((n) => n !== undefined)
|
|
795
791
|
.map((child) => {
|
|
796
792
|
if (child.type !== 'door' && child.type !== 'window')
|
|
797
793
|
return child;
|
|
798
|
-
// `getEffectiveNode` folds in resize overrides (width/height arrows).
|
|
799
|
-
// Position moves publish to `useLiveTransforms` instead, so fold that
|
|
800
|
-
// in too — opening cutout brushes are rebuilt directly from the
|
|
801
|
-
// effective node position rather than from the rendered proxy mesh.
|
|
802
794
|
const effective = getEffectiveNode(child);
|
|
803
795
|
const live = useLiveTransforms.getState().get(child.id);
|
|
804
|
-
|
|
805
|
-
return effective;
|
|
806
|
-
return { ...effective, position: live.position };
|
|
796
|
+
return live?.position ? { ...effective, position: live.position } : effective;
|
|
807
797
|
});
|
|
808
|
-
const
|
|
798
|
+
const prepared = geometryAdapter?.prepareChildren?.(node, childrenNodes, {
|
|
799
|
+
isLive: (id) => useLiveNodeOverrides.getState().get(id) !== undefined ||
|
|
800
|
+
useLiveTransforms.getState().get(id) !== undefined,
|
|
801
|
+
}) ?? {
|
|
802
|
+
envelopeChildren: childrenNodes,
|
|
803
|
+
renderChildren: childrenNodes,
|
|
804
|
+
};
|
|
805
|
+
const builtGeo = generateExtrudedWall(node, prepared.envelopeChildren, miterData, slabElevation, slabSupport.baseElevation, slabSupport.baseSegments, planeTop, terrainBottomAt);
|
|
809
806
|
const wallAngle = Math.atan2(node.end[1] - node.start[1], node.end[0] - node.start[0]);
|
|
810
807
|
// World transform the render mesh will apply (position + Y-rotation below).
|
|
811
808
|
// Reproduce it here so the UVs can be projected in WORLD space — see
|
|
812
809
|
// `applyWorldPlanarWallUVs`.
|
|
813
810
|
const wallWorldMatrix = new THREE.Matrix4().compose(new THREE.Vector3(node.start[0], slabElevation, node.start[1]), new THREE.Quaternion().setFromAxisAngle(WALL_UV_Y_AXIS, -wallAngle), WALL_UV_UNIT_SCALE);
|
|
814
|
-
const
|
|
811
|
+
const renderedGeo = geometryAdapter?.buildGeometry?.(node, builtGeo, prepared.renderChildren) ?? builtGeo;
|
|
812
|
+
const newGeo = applyWorldPlanarWallUVs(renderedGeo, wallWorldMatrix);
|
|
815
813
|
mesh.geometry.dispose();
|
|
816
814
|
// A degenerate rebuild (zero-length or fully cut wall) yields as few vertices
|
|
817
815
|
// as the mount-time placeholder; the stamp keeps the sweep from re-marking it.
|
|
818
816
|
newGeo.userData.built = true;
|
|
819
817
|
mesh.geometry = newGeo;
|
|
818
|
+
geometryAdapter?.syncAuxiliaryGeometry?.(node, mesh, newGeo);
|
|
820
819
|
// Update collision mesh
|
|
821
820
|
const collisionMesh = mesh.getObjectByName('collision-mesh');
|
|
822
821
|
if (collisionMesh) {
|
|
@@ -10,7 +10,7 @@ export declare const DOUBLE_HUNG_BOTTOM_SASH_NAME = "double-hung-bottom-sash";
|
|
|
10
10
|
export declare const LOUVERED_WINDOW_SLATS_NAME = "louvered-window-slats";
|
|
11
11
|
export declare const AWNING_WINDOW_SASH_NAME = "awning-window-sash";
|
|
12
12
|
export declare const HOPPER_WINDOW_SASH_NAME = "hopper-window-sash";
|
|
13
|
-
export declare const pendingWindowAnimationRebuilds: Set<`building_${string}` | `level_${string}` | `elevator_${string}` | `
|
|
13
|
+
export declare const pendingWindowAnimationRebuilds: Set<`building_${string}` | `level_${string}` | `elevator_${string}` | `unit_${string}` | `block_${string}` | `ceiling_${string}` | `item_${string}` | `procedural-item_${string}` | `column_${string}` | `construction-dimension_${string}` | `wall_${string}` | `roof_${string}` | `slab_${string}` | `fence_${string}` | `structural-grid_${string}` | `imesh_${string}` | `zone_${string}` | `stair_${string}` | `scan_${string}` | `guide_${string}` | `measurement_${string}` | `spawn_${string}` | `shelf_${string}` | `duct-segment_${string}` | `duct-fitting_${string}` | `duct-terminal_${string}` | `hvac-equipment_${string}` | `lineset_${string}` | `liquid-line_${string}` | `pipe-segment_${string}` | `pipe-fitting_${string}` | `pipe-trap_${string}` | `site_${string}` | `leanto_${string}` | `door_${string}` | `window_${string}` | `cabinet_${string}` | `cabinet-module_${string}` | `rseg_${string}` | `sseg_${string}` | `bvent_${string}` | `rvent_${string}` | `tvent_${string}` | `cupola_${string}` | `eyebrow-vent_${string}` | `gutter_${string}` | `chimney_${string}` | `solarpanel_${string}` | `skylight_${string}` | `dormer_${string}` | `downspout_${string}`>;
|
|
14
14
|
export declare const WindowSystem: () => null;
|
|
15
15
|
/**
|
|
16
16
|
* Build a fresh window mesh for preview/ghost rendering.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"window-system.d.ts","sourceRoot":"","sources":["../../../src/systems/window/window-system.tsx"],"names":[],"mappings":"AAAA,OAAO,EAYL,KAAK,UAAU,EAChB,MAAM,kBAAkB,CAAA;AAGzB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAyB9B,eAAO,MAAM,yBAAyB,yBAAyB,CAAA;AAC/D,eAAO,MAAM,8BAA8B,8BAA8B,CAAA;AACzE,eAAO,MAAM,+BAA+B,+BAA+B,CAAA;AAC3E,eAAO,MAAM,gCAAgC,gCAAgC,CAAA;AAC7E,eAAO,MAAM,4BAA4B,4BAA4B,CAAA;AACrE,eAAO,MAAM,yBAAyB,yBAAyB,CAAA;AAC/D,eAAO,MAAM,4BAA4B,4BAA4B,CAAA;AACrE,eAAO,MAAM,0BAA0B,0BAA0B,CAAA;AACjE,eAAO,MAAM,uBAAuB,uBAAuB,CAAA;AAC3D,eAAO,MAAM,uBAAuB,uBAAuB,CAAA;AAS3D,eAAO,MAAM,8BAA8B,
|
|
1
|
+
{"version":3,"file":"window-system.d.ts","sourceRoot":"","sources":["../../../src/systems/window/window-system.tsx"],"names":[],"mappings":"AAAA,OAAO,EAYL,KAAK,UAAU,EAChB,MAAM,kBAAkB,CAAA;AAGzB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAyB9B,eAAO,MAAM,yBAAyB,yBAAyB,CAAA;AAC/D,eAAO,MAAM,8BAA8B,8BAA8B,CAAA;AACzE,eAAO,MAAM,+BAA+B,+BAA+B,CAAA;AAC3E,eAAO,MAAM,gCAAgC,gCAAgC,CAAA;AAC7E,eAAO,MAAM,4BAA4B,4BAA4B,CAAA;AACrE,eAAO,MAAM,yBAAyB,yBAAyB,CAAA;AAC/D,eAAO,MAAM,4BAA4B,4BAA4B,CAAA;AACrE,eAAO,MAAM,0BAA0B,0BAA0B,CAAA;AACjE,eAAO,MAAM,uBAAuB,uBAAuB,CAAA;AAC3D,eAAO,MAAM,uBAAuB,uBAAuB,CAAA;AAS3D,eAAO,MAAM,8BAA8B,uoCAAuB,CAAA;AAElE,eAAO,MAAM,YAAY,YAqHxB,CAAA;AA28GD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,UAAU,GAAG,KAAK,CAAC,IAAI,CAInE"}
|
|
@@ -136,8 +136,9 @@ export const WindowSystem = () => {
|
|
|
136
136
|
// Rebuild the parent wall so its cutout reflects the updated window geometry
|
|
137
137
|
// Avoid triggering expensive wall CSG rebuilds while the window is being interactively moved/duplicated.
|
|
138
138
|
// The editor tools will request a final wall rebuild on commit.
|
|
139
|
-
const
|
|
140
|
-
|
|
139
|
+
const metadata = effectiveNode.metadata;
|
|
140
|
+
const deferParentRebuild = !!metadata?.isTransient || !!metadata?.deferParentRebuild;
|
|
141
|
+
if (!deferParentRebuild && effectiveNode.parentId) {
|
|
141
142
|
useScene.getState().dirtyNodes.add(effectiveNode.parentId);
|
|
142
143
|
}
|
|
143
144
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zone-system.d.ts","sourceRoot":"","sources":["../../../src/systems/zone/zone-system.tsx"],"names":[],"mappings":"AAUA,eAAO,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"zone-system.d.ts","sourceRoot":"","sources":["../../../src/systems/zone/zone-system.tsx"],"names":[],"mappings":"AAUA,eAAO,MAAM,UAAU,YAqHtB,CAAA"}
|
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
import { sceneRegistry, useScene } from '@pascal-app/core';
|
|
2
2
|
import { useFrame } from '@react-three/fiber';
|
|
3
|
-
import { useRef } from 'react';
|
|
3
|
+
import { useMemo, useRef } from 'react';
|
|
4
4
|
import { MathUtils } from 'three';
|
|
5
5
|
import useViewer from '../../store/use-viewer';
|
|
6
6
|
const TRANSITION_DURATION = 400; // ms
|
|
7
7
|
const EXIT_DEBOUNCE_MS = 50; // ignore rapid exit→re-enter within this window
|
|
8
8
|
export const ZoneSystem = () => {
|
|
9
|
+
const focusedUnitId = useViewer((state) => state.focusedUnitId);
|
|
10
|
+
const focusedUnitMembers = useScene((state) => {
|
|
11
|
+
const unit = focusedUnitId ? state.nodes[focusedUnitId] : undefined;
|
|
12
|
+
return unit?.type === 'unit' ? unit.members : undefined;
|
|
13
|
+
});
|
|
14
|
+
const focusedZoneIds = useMemo(() => new Set(focusedUnitMembers), [focusedUnitMembers]);
|
|
15
|
+
const selection = useViewer((state) => state.selection);
|
|
16
|
+
const showZones = useViewer((state) => state.showZones);
|
|
17
|
+
const lastFocusRef = useRef(null);
|
|
18
|
+
const lastFocusedZoneIdsRef = useRef(null);
|
|
19
|
+
const lastSelectionRef = useRef(null);
|
|
20
|
+
const lastShowZonesRef = useRef(showZones);
|
|
9
21
|
const lastHighlightedZoneRef = useRef(null);
|
|
10
22
|
const lastChangeTimeRef = useRef(0);
|
|
11
23
|
const isTransitioningRef = useRef(false);
|
|
@@ -49,6 +61,17 @@ export const ZoneSystem = () => {
|
|
|
49
61
|
lastChangeTimeRef.current = clock.elapsedTime * 1000;
|
|
50
62
|
isTransitioningRef.current = true;
|
|
51
63
|
}
|
|
64
|
+
if (focusedUnitId !== lastFocusRef.current ||
|
|
65
|
+
focusedZoneIds !== lastFocusedZoneIdsRef.current ||
|
|
66
|
+
selection !== lastSelectionRef.current ||
|
|
67
|
+
showZones !== lastShowZonesRef.current) {
|
|
68
|
+
lastFocusRef.current = focusedUnitId;
|
|
69
|
+
lastFocusedZoneIdsRef.current = focusedZoneIds;
|
|
70
|
+
lastSelectionRef.current = selection;
|
|
71
|
+
lastShowZonesRef.current = showZones;
|
|
72
|
+
lastChangeTimeRef.current = clock.elapsedTime * 1000;
|
|
73
|
+
isTransitioningRef.current = true;
|
|
74
|
+
}
|
|
52
75
|
// Skip frame if not transitioning
|
|
53
76
|
if (!isTransitioningRef.current)
|
|
54
77
|
return;
|
|
@@ -63,7 +86,10 @@ export const ZoneSystem = () => {
|
|
|
63
86
|
const zone = sceneRegistry.nodes.get(zoneId);
|
|
64
87
|
if (!zone)
|
|
65
88
|
return;
|
|
66
|
-
const isHighlighted = zoneId === highlightedZone
|
|
89
|
+
const isHighlighted = zoneId === highlightedZone ||
|
|
90
|
+
zoneId === selection.zoneId ||
|
|
91
|
+
selection.selectedIds.includes(zoneId) ||
|
|
92
|
+
focusedZoneIds.has(zoneId);
|
|
67
93
|
const targetOpacity = isHighlighted ? 1 : 0;
|
|
68
94
|
const walls = zone.getObjectByName('walls');
|
|
69
95
|
if (walls) {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
export declare function ImmersiveXRPresentationProvider({ children, enabled, }: {
|
|
3
|
+
children: ReactNode;
|
|
4
|
+
enabled: boolean;
|
|
5
|
+
}): import("react").JSX.Element;
|
|
6
|
+
export declare function useImmersiveXRPresentation(): boolean;
|
|
7
|
+
//# sourceMappingURL=presentation-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presentation-context.d.ts","sourceRoot":"","sources":["../../src/xr/presentation-context.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAiB,KAAK,SAAS,EAAc,MAAM,OAAO,CAAA;AAIjE,wBAAgB,+BAA+B,CAAC,EAC9C,QAAQ,EACR,OAAO,GACR,EAAE;IACD,QAAQ,EAAE,SAAS,CAAA;IACnB,OAAO,EAAE,OAAO,CAAA;CACjB,+BAMA;AAED,wBAAgB,0BAA0B,YAEzC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useContext } from 'react';
|
|
4
|
+
const ImmersiveXRPresentationContext = createContext(false);
|
|
5
|
+
export function ImmersiveXRPresentationProvider({ children, enabled, }) {
|
|
6
|
+
return (_jsx(ImmersiveXRPresentationContext.Provider, { value: enabled, children: children }));
|
|
7
|
+
}
|
|
8
|
+
export function useImmersiveXRPresentation() {
|
|
9
|
+
return useContext(ImmersiveXRPresentationContext);
|
|
10
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pascal-app/viewer",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "3D viewer component for Pascal building editor",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"prepublishOnly": "npm run build"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
|
-
"@pascal-app/core": "^1.0.
|
|
36
|
+
"@pascal-app/core": "^1.0.3",
|
|
37
37
|
"@react-three/drei": "^10",
|
|
38
38
|
"@react-three/fiber": "^9",
|
|
39
39
|
"react": "^18 || ^19",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"zustand": "^5"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@pascal-app/core": "^1.0.
|
|
49
|
+
"@pascal-app/core": "^1.0.3",
|
|
50
50
|
"@pascal/typescript-config": "*",
|
|
51
51
|
"@react-three/test-renderer": "^9.1.0",
|
|
52
52
|
"@types/node": "^22",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
],
|
|
68
68
|
"repository": {
|
|
69
69
|
"type": "git",
|
|
70
|
-
"url": "https://github.com/pascalorg/editor.git",
|
|
70
|
+
"url": "git+https://github.com/pascalorg/editor.git",
|
|
71
71
|
"directory": "packages/viewer"
|
|
72
72
|
},
|
|
73
73
|
"license": "MIT",
|