@pascal-app/viewer 0.1.13 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/error-boundary.d.ts +18 -0
- package/dist/components/error-boundary.d.ts.map +1 -0
- package/dist/components/error-boundary.js +11 -0
- package/dist/components/renderers/ceiling/ceiling-renderer.d.ts.map +1 -1
- package/dist/components/renderers/ceiling/ceiling-renderer.js +16 -9
- package/dist/components/renderers/door/door-renderer.d.ts +5 -0
- package/dist/components/renderers/door/door-renderer.d.ts.map +1 -0
- package/dist/components/renderers/door/door-renderer.js +11 -0
- package/dist/components/renderers/guide/guide-renderer.d.ts.map +1 -1
- package/dist/components/renderers/guide/guide-renderer.js +4 -2
- package/dist/components/renderers/item/item-renderer.d.ts.map +1 -1
- package/dist/components/renderers/item/item-renderer.js +98 -7
- package/dist/components/renderers/node-renderer.d.ts.map +1 -1
- package/dist/components/renderers/node-renderer.js +3 -1
- package/dist/components/renderers/roof/roof-materials.d.ts +4 -0
- package/dist/components/renderers/roof/roof-materials.d.ts.map +1 -0
- package/dist/components/renderers/roof/roof-materials.js +16 -0
- package/dist/components/renderers/roof/roof-renderer.d.ts.map +1 -1
- package/dist/components/renderers/roof/roof-renderer.js +5 -1
- package/dist/components/renderers/roof-segment/roof-segment-renderer.d.ts +5 -0
- package/dist/components/renderers/roof-segment/roof-segment-renderer.d.ts.map +1 -0
- package/dist/components/renderers/roof-segment/roof-segment-renderer.js +13 -0
- package/dist/components/renderers/scan/scan-renderer.d.ts.map +1 -1
- package/dist/components/renderers/scan/scan-renderer.js +3 -1
- package/dist/components/renderers/scene-renderer.d.ts.map +1 -1
- package/dist/components/renderers/scene-renderer.js +3 -3
- package/dist/components/renderers/site/site-renderer.d.ts.map +1 -1
- package/dist/components/renderers/site/site-renderer.js +4 -19
- package/dist/components/renderers/slab/slab-renderer.js +1 -1
- package/dist/components/renderers/wall/wall-renderer.d.ts.map +1 -1
- package/dist/components/renderers/wall/wall-renderer.js +7 -3
- package/dist/components/renderers/window/window-renderer.d.ts.map +1 -1
- package/dist/components/renderers/window/window-renderer.js +2 -1
- package/dist/components/renderers/zone/zone-renderer.d.ts.map +1 -1
- package/dist/components/renderers/zone/zone-renderer.js +33 -13
- package/dist/components/viewer/ground-occluder.d.ts +2 -0
- package/dist/components/viewer/ground-occluder.d.ts.map +1 -0
- package/dist/components/viewer/ground-occluder.js +75 -0
- package/dist/components/viewer/index.d.ts +1 -0
- package/dist/components/viewer/index.d.ts.map +1 -1
- package/dist/components/viewer/index.js +59 -6
- package/dist/components/viewer/lights.d.ts.map +1 -1
- package/dist/components/viewer/lights.js +69 -5
- package/dist/components/viewer/perf-monitor.d.ts +2 -0
- package/dist/components/viewer/perf-monitor.d.ts.map +1 -0
- package/dist/components/viewer/perf-monitor.js +42 -0
- package/dist/components/viewer/post-processing.d.ts.map +1 -1
- package/dist/components/viewer/post-processing.js +230 -107
- package/dist/components/viewer/selection-manager.d.ts.map +1 -1
- package/dist/components/viewer/selection-manager.js +47 -17
- package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
- package/dist/components/viewer/viewer-camera.js +2 -2
- package/dist/hooks/use-gltf-ktx2.d.ts +1 -1
- package/dist/hooks/use-gltf-ktx2.d.ts.map +1 -1
- package/dist/hooks/use-gltf-ktx2.js +25 -7
- package/dist/hooks/use-grid-events.d.ts +12 -0
- package/dist/hooks/use-grid-events.d.ts.map +1 -0
- package/dist/hooks/use-grid-events.js +33 -0
- package/dist/hooks/use-node-events.d.ts +9 -1
- package/dist/hooks/use-node-events.d.ts.map +1 -1
- package/dist/hooks/use-node-events.js +5 -5
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/lib/asset-url.d.ts +1 -1
- package/dist/lib/asset-url.d.ts.map +1 -1
- package/dist/lib/asset-url.js +1 -1
- package/dist/lib/layers.d.ts +5 -0
- package/dist/lib/layers.d.ts.map +1 -0
- package/dist/lib/layers.js +4 -0
- package/dist/store/use-item-light-pool.d.ts +18 -0
- package/dist/store/use-item-light-pool.d.ts.map +1 -0
- package/dist/store/use-item-light-pool.js +30 -0
- package/dist/store/use-viewer.d.ts +56 -7
- package/dist/store/use-viewer.d.ts.map +1 -1
- package/dist/store/use-viewer.js +82 -17
- package/dist/systems/interactive/interactive-system.d.ts +2 -0
- package/dist/systems/interactive/interactive-system.d.ts.map +1 -0
- package/dist/systems/interactive/interactive-system.js +95 -0
- package/dist/systems/item-light/item-light-system.d.ts +2 -0
- package/dist/systems/item-light/item-light-system.d.ts.map +1 -0
- package/dist/systems/item-light/item-light-system.js +235 -0
- package/dist/systems/level/level-system.d.ts.map +1 -1
- package/dist/systems/level/level-system.js +19 -8
- package/dist/systems/level/level-utils.d.ts +17 -0
- package/dist/systems/level/level-utils.d.ts.map +1 -0
- package/dist/systems/level/level-utils.js +82 -0
- package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
- package/dist/systems/wall/wall-cutout.js +2 -4
- package/dist/systems/zone/zone-system.d.ts.map +1 -1
- package/dist/systems/zone/zone-system.js +29 -3
- package/package.json +7 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-node-events.d.ts","sourceRoot":"","sources":["../../src/hooks/use-node-events.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,WAAW,
|
|
1
|
+
{"version":3,"file":"use-node-events.d.ts","sourceRoot":"","sources":["../../src/hooks/use-node-events.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,QAAQ,EAGb,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,QAAQ,EACd,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAGpD,KAAK,UAAU,GAAG;IAChB,IAAI,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,KAAK,EAAE,SAAS,CAAA;KAAE,CAAA;IAC1C,IAAI,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,KAAK,EAAE,SAAS,CAAA;KAAE,CAAA;IAC1C,IAAI,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,KAAK,EAAE,SAAS,CAAA;KAAE,CAAA;IAC1C,QAAQ,EAAE;QAAE,IAAI,EAAE,YAAY,CAAC;QAAC,KAAK,EAAE,aAAa,CAAA;KAAE,CAAA;IACtD,KAAK,EAAE;QAAE,IAAI,EAAE,SAAS,CAAC;QAAC,KAAK,EAAE,UAAU,CAAA;KAAE,CAAA;IAC7C,IAAI,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,KAAK,EAAE,SAAS,CAAA;KAAE,CAAA;IAC1C,IAAI,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,KAAK,EAAE,SAAS,CAAA;KAAE,CAAA;IAC1C,OAAO,EAAE;QAAE,IAAI,EAAE,WAAW,CAAC;QAAC,KAAK,EAAE,YAAY,CAAA;KAAE,CAAA;IACnD,IAAI,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,KAAK,EAAE,SAAS,CAAA;KAAE,CAAA;IAC1C,cAAc,EAAE;QAAE,IAAI,EAAE,eAAe,CAAC;QAAC,KAAK,EAAE,gBAAgB,CAAA;KAAE,CAAA;IAClE,MAAM,EAAE;QAAE,IAAI,EAAE,UAAU,CAAC;QAAC,KAAK,EAAE,WAAW,CAAA;KAAE,CAAA;IAChD,IAAI,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,KAAK,EAAE,SAAS,CAAA;KAAE,CAAA;CAC3C,CAAA;AAED,KAAK,QAAQ,GAAG,MAAM,UAAU,CAAA;AAEhC,wBAAgB,aAAa,CAAC,CAAC,SAAS,QAAQ,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC;uBAiB/D,UAAU,CAAC,YAAY,CAAC;qBAK1B,UAAU,CAAC,YAAY,CAAC;iBAQ5B,UAAU,CAAC,YAAY,CAAC;wBAIjB,UAAU,CAAC,YAAY,CAAC;wBAIxB,UAAU,CAAC,YAAY,CAAC;uBAIzB,UAAU,CAAC,YAAY,CAAC;uBAIxB,UAAU,CAAC,YAAY,CAAC;uBAIxB,UAAU,CAAC,YAAY,CAAC;EAK9C"}
|
|
@@ -28,13 +28,13 @@ export function useNodeEvents(node, type) {
|
|
|
28
28
|
if (e.button !== 0)
|
|
29
29
|
return;
|
|
30
30
|
emit('pointerup', e);
|
|
31
|
+
// Synthesize a click event on pointer up to be more forgiving than R3F's default onClick
|
|
32
|
+
// which often fails if the mouse moves even 1 pixel.
|
|
33
|
+
emit('click', e);
|
|
31
34
|
},
|
|
32
35
|
onClick: (e) => {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (e.button !== 0)
|
|
36
|
-
return;
|
|
37
|
-
emit('click', e);
|
|
36
|
+
// Disable default R3F click since we synthesize it on pointerup
|
|
37
|
+
// This prevents double-clicks from firing twice.
|
|
38
38
|
},
|
|
39
39
|
onPointerEnter: (e) => {
|
|
40
40
|
if (useViewer.getState().cameraDragging)
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export { default as Viewer } from './components/viewer';
|
|
2
|
-
export { default as useViewer } from './store/use-viewer';
|
|
3
2
|
export { ASSETS_CDN_URL, resolveAssetUrl, resolveCdnUrl } from './lib/asset-url';
|
|
3
|
+
export { SCENE_LAYER, ZONE_LAYER } from './lib/layers';
|
|
4
|
+
export { default as useViewer } from './store/use-viewer';
|
|
5
|
+
export { InteractiveSystem } from './systems/interactive/interactive-system';
|
|
6
|
+
export { snapLevelsToTruePositions } from './systems/level/level-utils';
|
|
4
7
|
//# 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":"AAAA,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAChF,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACtD,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0CAA0C,CAAA;AAC5E,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
export { default as Viewer } from './components/viewer';
|
|
2
|
-
export { default as useViewer } from './store/use-viewer';
|
|
3
2
|
export { ASSETS_CDN_URL, resolveAssetUrl, resolveCdnUrl } from './lib/asset-url';
|
|
3
|
+
export { SCENE_LAYER, ZONE_LAYER } from './lib/layers';
|
|
4
|
+
export { default as useViewer } from './store/use-viewer';
|
|
5
|
+
export { InteractiveSystem } from './systems/interactive/interactive-system';
|
|
6
|
+
export { snapLevelsToTruePositions } from './systems/level/level-utils';
|
package/dist/lib/asset-url.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asset-url.d.ts","sourceRoot":"","sources":["../../src/lib/asset-url.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,cAAc,
|
|
1
|
+
{"version":3,"file":"asset-url.d.ts","sourceRoot":"","sources":["../../src/lib/asset-url.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,cAAc,QAAwE,CAAA;AAEnG;;;;;;GAMG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgB5F;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAiB3E"}
|
package/dist/lib/asset-url.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { loadAssetUrl } from '@pascal-app/core';
|
|
2
|
-
export const ASSETS_CDN_URL = 'https://editor.pascal.app';
|
|
2
|
+
export const ASSETS_CDN_URL = process.env.NEXT_PUBLIC_ASSETS_CDN_URL || 'https://editor.pascal.app';
|
|
3
3
|
/**
|
|
4
4
|
* Resolves an asset URL to the appropriate format:
|
|
5
5
|
* - If URL starts with http:// or https://, return as-is (external URL)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layers.d.ts","sourceRoot":"","sources":["../../src/lib/layers.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,eAAO,MAAM,WAAW,IAAI,CAAA;AAE5B,oEAAoE;AACpE,eAAO,MAAM,UAAU,IAAI,CAAA"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { AnyNodeId, Interactive, LightEffect } from '@pascal-app/core';
|
|
2
|
+
export type LightRegistration = {
|
|
3
|
+
nodeId: AnyNodeId;
|
|
4
|
+
effect: LightEffect;
|
|
5
|
+
toggleIndex: number;
|
|
6
|
+
sliderIndex: number;
|
|
7
|
+
sliderMin: number;
|
|
8
|
+
sliderMax: number;
|
|
9
|
+
hasSlider: boolean;
|
|
10
|
+
};
|
|
11
|
+
type ItemLightPoolStore = {
|
|
12
|
+
registrations: Map<string, LightRegistration>;
|
|
13
|
+
register: (key: string, nodeId: AnyNodeId, effect: LightEffect, interactive: Interactive) => void;
|
|
14
|
+
unregister: (key: string) => void;
|
|
15
|
+
};
|
|
16
|
+
export declare const useItemLightPool: import("zustand").UseBoundStore<import("zustand").StoreApi<ItemLightPoolStore>>;
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=use-item-light-pool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-item-light-pool.d.ts","sourceRoot":"","sources":["../../src/store/use-item-light-pool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,EAAiB,MAAM,kBAAkB,CAAA;AAG1F,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,SAAS,CAAA;IACjB,MAAM,EAAE,WAAW,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,OAAO,CAAA;CACnB,CAAA;AAED,KAAK,kBAAkB,GAAG;IACxB,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAA;IAC7C,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,KAAK,IAAI,CAAA;IACjG,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;CAClC,CAAA;AAED,eAAO,MAAM,gBAAgB,iFAiC1B,CAAA"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { create } from 'zustand';
|
|
2
|
+
export const useItemLightPool = create((set) => ({
|
|
3
|
+
registrations: new Map(),
|
|
4
|
+
register: (key, nodeId, effect, interactive) => {
|
|
5
|
+
const toggleIndex = interactive.controls.findIndex((c) => c.kind === 'toggle');
|
|
6
|
+
const sliderIndex = interactive.controls.findIndex((c) => c.kind === 'slider');
|
|
7
|
+
const sliderControl = sliderIndex >= 0 ? interactive.controls[sliderIndex] : null;
|
|
8
|
+
const registration = {
|
|
9
|
+
nodeId,
|
|
10
|
+
effect,
|
|
11
|
+
toggleIndex,
|
|
12
|
+
sliderIndex,
|
|
13
|
+
hasSlider: sliderControl !== null,
|
|
14
|
+
sliderMin: sliderControl?.min ?? 0,
|
|
15
|
+
sliderMax: sliderControl?.max ?? 1,
|
|
16
|
+
};
|
|
17
|
+
set((s) => {
|
|
18
|
+
const next = new Map(s.registrations);
|
|
19
|
+
next.set(key, registration);
|
|
20
|
+
return { registrations: next };
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
unregister: (key) => {
|
|
24
|
+
set((s) => {
|
|
25
|
+
const next = new Map(s.registrations);
|
|
26
|
+
next.delete(key);
|
|
27
|
+
return { registrations: next };
|
|
28
|
+
});
|
|
29
|
+
},
|
|
30
|
+
}));
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type { AnyNode, BaseNode, BuildingNode, LevelNode, ZoneNode } from
|
|
2
|
-
import type { Object3D } from
|
|
1
|
+
import type { AnyNode, BaseNode, BuildingNode, LevelNode, ZoneNode } from '@pascal-app/core';
|
|
2
|
+
import type { Object3D } from 'three';
|
|
3
3
|
type SelectionPath = {
|
|
4
|
-
buildingId: BuildingNode[
|
|
5
|
-
levelId: LevelNode[
|
|
6
|
-
zoneId: ZoneNode[
|
|
7
|
-
selectedIds: BaseNode[
|
|
4
|
+
buildingId: BuildingNode['id'] | null;
|
|
5
|
+
levelId: LevelNode['id'] | null;
|
|
6
|
+
zoneId: ZoneNode['id'] | null;
|
|
7
|
+
selectedIds: BaseNode['id'][];
|
|
8
8
|
};
|
|
9
9
|
type Outliner = {
|
|
10
10
|
selectedObjects: Object3D[];
|
|
@@ -16,6 +16,10 @@ type ViewerState = {
|
|
|
16
16
|
setHoveredId: (id: AnyNode['id'] | ZoneNode['id'] | null) => void;
|
|
17
17
|
cameraMode: 'perspective' | 'orthographic';
|
|
18
18
|
setCameraMode: (mode: 'perspective' | 'orthographic') => void;
|
|
19
|
+
theme: 'light' | 'dark';
|
|
20
|
+
setTheme: (theme: 'light' | 'dark') => void;
|
|
21
|
+
unit: 'metric' | 'imperial';
|
|
22
|
+
setUnit: (unit: 'metric' | 'imperial') => void;
|
|
19
23
|
levelMode: 'stacked' | 'exploded' | 'solo' | 'manual';
|
|
20
24
|
setLevelMode: (mode: 'stacked' | 'exploded' | 'solo' | 'manual') => void;
|
|
21
25
|
wallMode: 'up' | 'cutaway' | 'down';
|
|
@@ -24,14 +28,59 @@ type ViewerState = {
|
|
|
24
28
|
setShowScans: (show: boolean) => void;
|
|
25
29
|
showGuides: boolean;
|
|
26
30
|
setShowGuides: (show: boolean) => void;
|
|
31
|
+
showGrid: boolean;
|
|
32
|
+
setShowGrid: (show: boolean) => void;
|
|
33
|
+
projectId: string | null;
|
|
34
|
+
setProjectId: (id: string | null) => void;
|
|
35
|
+
projectPreferences: Record<string, {
|
|
36
|
+
showScans?: boolean;
|
|
37
|
+
showGuides?: boolean;
|
|
38
|
+
showGrid?: boolean;
|
|
39
|
+
}>;
|
|
27
40
|
setSelection: (updates: Partial<SelectionPath>) => void;
|
|
28
41
|
resetSelection: () => void;
|
|
29
42
|
outliner: Outliner;
|
|
30
43
|
exportScene: (() => Promise<void>) | null;
|
|
31
44
|
setExportScene: (fn: (() => Promise<void>) | null) => void;
|
|
45
|
+
debugColors: boolean;
|
|
46
|
+
setDebugColors: (enabled: boolean) => void;
|
|
32
47
|
cameraDragging: boolean;
|
|
33
48
|
setCameraDragging: (dragging: boolean) => void;
|
|
34
49
|
};
|
|
35
|
-
declare const useViewer: import("zustand").UseBoundStore<import("zustand").StoreApi<ViewerState
|
|
50
|
+
declare const useViewer: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<ViewerState>, "setState" | "persist"> & {
|
|
51
|
+
setState(partial: ViewerState | Partial<ViewerState> | ((state: ViewerState) => ViewerState | Partial<ViewerState>), replace?: false | undefined): unknown;
|
|
52
|
+
setState(state: ViewerState | ((state: ViewerState) => ViewerState), replace: true): unknown;
|
|
53
|
+
persist: {
|
|
54
|
+
setOptions: (options: Partial<import("zustand/middleware").PersistOptions<ViewerState, {
|
|
55
|
+
cameraMode: "perspective" | "orthographic";
|
|
56
|
+
theme: "light" | "dark";
|
|
57
|
+
unit: "metric" | "imperial";
|
|
58
|
+
levelMode: "stacked" | "exploded" | "solo" | "manual";
|
|
59
|
+
wallMode: "up" | "cutaway" | "down";
|
|
60
|
+
projectPreferences: Record<string, {
|
|
61
|
+
showScans?: boolean;
|
|
62
|
+
showGuides?: boolean;
|
|
63
|
+
showGrid?: boolean;
|
|
64
|
+
}>;
|
|
65
|
+
}, unknown>>) => void;
|
|
66
|
+
clearStorage: () => void;
|
|
67
|
+
rehydrate: () => Promise<void> | void;
|
|
68
|
+
hasHydrated: () => boolean;
|
|
69
|
+
onHydrate: (fn: (state: ViewerState) => void) => () => void;
|
|
70
|
+
onFinishHydration: (fn: (state: ViewerState) => void) => () => void;
|
|
71
|
+
getOptions: () => Partial<import("zustand/middleware").PersistOptions<ViewerState, {
|
|
72
|
+
cameraMode: "perspective" | "orthographic";
|
|
73
|
+
theme: "light" | "dark";
|
|
74
|
+
unit: "metric" | "imperial";
|
|
75
|
+
levelMode: "stacked" | "exploded" | "solo" | "manual";
|
|
76
|
+
wallMode: "up" | "cutaway" | "down";
|
|
77
|
+
projectPreferences: Record<string, {
|
|
78
|
+
showScans?: boolean;
|
|
79
|
+
showGuides?: boolean;
|
|
80
|
+
showGrid?: boolean;
|
|
81
|
+
}>;
|
|
82
|
+
}, unknown>>;
|
|
83
|
+
};
|
|
84
|
+
}>;
|
|
36
85
|
export default useViewer;
|
|
37
86
|
//# sourceMappingURL=use-viewer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-viewer.d.ts","sourceRoot":"","sources":["../../src/store/use-viewer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"use-viewer.d.ts","sourceRoot":"","sources":["../../src/store/use-viewer.ts"],"names":[],"mappings":"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"}
|
package/dist/store/use-viewer.js
CHANGED
|
@@ -1,33 +1,86 @@
|
|
|
1
|
-
|
|
2
|
-
import { create } from
|
|
3
|
-
|
|
1
|
+
'use client';
|
|
2
|
+
import { create } from 'zustand';
|
|
3
|
+
import { persist } from 'zustand/middleware';
|
|
4
|
+
const useViewer = create()(persist((set) => ({
|
|
4
5
|
selection: { buildingId: null, levelId: null, zoneId: null, selectedIds: [] },
|
|
5
6
|
hoveredId: null,
|
|
6
7
|
setHoveredId: (id) => set({ hoveredId: id }),
|
|
7
|
-
cameraMode:
|
|
8
|
+
cameraMode: 'perspective',
|
|
8
9
|
setCameraMode: (mode) => set({ cameraMode: mode }),
|
|
9
|
-
|
|
10
|
+
theme: 'light',
|
|
11
|
+
setTheme: (theme) => set({ theme }),
|
|
12
|
+
unit: 'metric',
|
|
13
|
+
setUnit: (unit) => set({ unit }),
|
|
14
|
+
levelMode: 'stacked',
|
|
10
15
|
setLevelMode: (mode) => set({ levelMode: mode }),
|
|
11
|
-
wallMode: '
|
|
16
|
+
wallMode: 'up',
|
|
12
17
|
setWallMode: (mode) => set({ wallMode: mode }),
|
|
13
18
|
showScans: true,
|
|
14
|
-
setShowScans: (show) => set(
|
|
19
|
+
setShowScans: (show) => set((state) => {
|
|
20
|
+
const projectPreferences = { ...(state.projectPreferences || {}) };
|
|
21
|
+
if (state.projectId) {
|
|
22
|
+
projectPreferences[state.projectId] = {
|
|
23
|
+
...(projectPreferences[state.projectId] || {}),
|
|
24
|
+
showScans: show,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
return { showScans: show, projectPreferences };
|
|
28
|
+
}),
|
|
15
29
|
showGuides: true,
|
|
16
|
-
setShowGuides: (show) => set(
|
|
30
|
+
setShowGuides: (show) => set((state) => {
|
|
31
|
+
const projectPreferences = { ...(state.projectPreferences || {}) };
|
|
32
|
+
if (state.projectId) {
|
|
33
|
+
projectPreferences[state.projectId] = {
|
|
34
|
+
...(projectPreferences[state.projectId] || {}),
|
|
35
|
+
showGuides: show,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return { showGuides: show, projectPreferences };
|
|
39
|
+
}),
|
|
40
|
+
showGrid: true,
|
|
41
|
+
setShowGrid: (show) => set((state) => {
|
|
42
|
+
const projectPreferences = { ...(state.projectPreferences || {}) };
|
|
43
|
+
if (state.projectId) {
|
|
44
|
+
projectPreferences[state.projectId] = {
|
|
45
|
+
...(projectPreferences[state.projectId] || {}),
|
|
46
|
+
showGrid: show,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return { showGrid: show, projectPreferences };
|
|
50
|
+
}),
|
|
51
|
+
projectId: null,
|
|
52
|
+
setProjectId: (id) => set((state) => {
|
|
53
|
+
if (!id)
|
|
54
|
+
return { projectId: id };
|
|
55
|
+
const prefs = state.projectPreferences?.[id] || {};
|
|
56
|
+
return {
|
|
57
|
+
projectId: id,
|
|
58
|
+
showScans: prefs.showScans ?? true,
|
|
59
|
+
showGuides: prefs.showGuides ?? true,
|
|
60
|
+
showGrid: prefs.showGrid ?? true,
|
|
61
|
+
};
|
|
62
|
+
}),
|
|
63
|
+
projectPreferences: {},
|
|
17
64
|
setSelection: (updates) => set((state) => {
|
|
18
65
|
const newSelection = { ...state.selection, ...updates };
|
|
19
|
-
// Hierarchy Guard: If we change a high-level parent, reset the children
|
|
66
|
+
// Hierarchy Guard: If we change a high-level parent, reset the children unless explicitly provided
|
|
20
67
|
if (updates.buildingId !== undefined) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
68
|
+
if (updates.levelId === undefined)
|
|
69
|
+
newSelection.levelId = null;
|
|
70
|
+
if (updates.zoneId === undefined)
|
|
71
|
+
newSelection.zoneId = null;
|
|
72
|
+
if (updates.selectedIds === undefined)
|
|
73
|
+
newSelection.selectedIds = [];
|
|
24
74
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
75
|
+
if (updates.levelId !== undefined) {
|
|
76
|
+
if (updates.zoneId === undefined)
|
|
77
|
+
newSelection.zoneId = null;
|
|
78
|
+
if (updates.selectedIds === undefined)
|
|
79
|
+
newSelection.selectedIds = [];
|
|
28
80
|
}
|
|
29
|
-
|
|
30
|
-
|
|
81
|
+
if (updates.zoneId !== undefined) {
|
|
82
|
+
if (updates.selectedIds === undefined)
|
|
83
|
+
newSelection.selectedIds = [];
|
|
31
84
|
}
|
|
32
85
|
return { selection: newSelection };
|
|
33
86
|
}),
|
|
@@ -42,7 +95,19 @@ const useViewer = create()((set, get) => ({
|
|
|
42
95
|
outliner: { selectedObjects: [], hoveredObjects: [] },
|
|
43
96
|
exportScene: null,
|
|
44
97
|
setExportScene: (fn) => set({ exportScene: fn }),
|
|
98
|
+
debugColors: false,
|
|
99
|
+
setDebugColors: (enabled) => set({ debugColors: enabled }),
|
|
45
100
|
cameraDragging: false,
|
|
46
101
|
setCameraDragging: (dragging) => set({ cameraDragging: dragging }),
|
|
102
|
+
}), {
|
|
103
|
+
name: 'viewer-preferences',
|
|
104
|
+
partialize: (state) => ({
|
|
105
|
+
cameraMode: state.cameraMode,
|
|
106
|
+
theme: state.theme,
|
|
107
|
+
unit: state.unit,
|
|
108
|
+
levelMode: state.levelMode,
|
|
109
|
+
wallMode: state.wallMode,
|
|
110
|
+
projectPreferences: state.projectPreferences,
|
|
111
|
+
}),
|
|
47
112
|
}));
|
|
48
113
|
export default useViewer;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interactive-system.d.ts","sourceRoot":"","sources":["../../../src/systems/interactive/interactive-system.tsx"],"names":[],"mappings":"AAwBA,eAAO,MAAM,iBAAiB,+CAgB7B,CAAA"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { pointInPolygon, sceneRegistry, useInteractive, useScene, } from '@pascal-app/core';
|
|
4
|
+
import { Html } from '@react-three/drei';
|
|
5
|
+
import { createPortal, useFrame } from '@react-three/fiber';
|
|
6
|
+
import { useState } from 'react';
|
|
7
|
+
import { Vector3 } from 'three';
|
|
8
|
+
import { useShallow } from 'zustand/react/shallow';
|
|
9
|
+
import useViewer from '../../store/use-viewer';
|
|
10
|
+
const _tempVec = new Vector3();
|
|
11
|
+
// ---- Parent: one overlay per interactive item ----
|
|
12
|
+
export const InteractiveSystem = () => {
|
|
13
|
+
const interactiveNodeIds = useScene(useShallow((state) => Object.values(state.nodes)
|
|
14
|
+
.filter((n) => n.type === 'item' && n.asset.interactive != null)
|
|
15
|
+
.map((n) => n.id)));
|
|
16
|
+
return (_jsx(_Fragment, { children: interactiveNodeIds.map((id) => (_jsx(ItemControlsOverlay, { nodeId: id }, id))) }));
|
|
17
|
+
};
|
|
18
|
+
// ---- Child: polls sceneRegistry then portals controls into the item group ----
|
|
19
|
+
const ItemControlsOverlay = ({ nodeId }) => {
|
|
20
|
+
const node = useScene((state) => state.nodes[nodeId]);
|
|
21
|
+
const [itemObj, setItemObj] = useState(null);
|
|
22
|
+
useFrame(() => {
|
|
23
|
+
if (itemObj)
|
|
24
|
+
return;
|
|
25
|
+
const obj = sceneRegistry.nodes.get(nodeId);
|
|
26
|
+
if (obj)
|
|
27
|
+
setItemObj(obj);
|
|
28
|
+
});
|
|
29
|
+
const controlValues = useInteractive(useShallow((state) => state.items[nodeId]?.controlValues));
|
|
30
|
+
const setControlValue = useInteractive((state) => state.setControlValue);
|
|
31
|
+
const zoneId = useViewer((s) => s.selection.zoneId);
|
|
32
|
+
const zonePolygon = useScene((s) => {
|
|
33
|
+
if (!zoneId)
|
|
34
|
+
return null;
|
|
35
|
+
const z = s.nodes[zoneId];
|
|
36
|
+
return z?.polygon ?? null;
|
|
37
|
+
});
|
|
38
|
+
if (!(itemObj && controlValues && node?.asset.interactive))
|
|
39
|
+
return null;
|
|
40
|
+
const { controls } = node.asset.interactive;
|
|
41
|
+
const [, height] = node.asset.dimensions;
|
|
42
|
+
let opacity = 0;
|
|
43
|
+
let pointerEvents = 'none';
|
|
44
|
+
if (zoneId && zonePolygon?.length) {
|
|
45
|
+
itemObj.getWorldPosition(_tempVec);
|
|
46
|
+
const inside = pointInPolygon(_tempVec.x, _tempVec.z, zonePolygon);
|
|
47
|
+
opacity = inside ? 1 : 0.1;
|
|
48
|
+
pointerEvents = inside ? 'auto' : 'none';
|
|
49
|
+
}
|
|
50
|
+
return createPortal(_jsx(Html, { center: true, distanceFactor: 8, occlude: true, position: [0, height + 0.3, 0], zIndexRange: [20, 0], children: _jsx("div", { style: {
|
|
51
|
+
display: 'flex',
|
|
52
|
+
flexDirection: 'column',
|
|
53
|
+
gap: 6,
|
|
54
|
+
background: 'rgba(0,0,0,0.75)',
|
|
55
|
+
backdropFilter: 'blur(8px)',
|
|
56
|
+
borderRadius: 8,
|
|
57
|
+
padding: '8px 12px',
|
|
58
|
+
minWidth: 120,
|
|
59
|
+
pointerEvents,
|
|
60
|
+
userSelect: 'none',
|
|
61
|
+
opacity,
|
|
62
|
+
transition: 'opacity 0.3s ease',
|
|
63
|
+
}, children: controls.map((control, i) => (_jsx(ControlWidget, { control: control, onChange: (v) => setControlValue(nodeId, i, v), value: controlValues[i] ?? false }, i))) }) }), itemObj);
|
|
64
|
+
};
|
|
65
|
+
// ---- Control widgets ----
|
|
66
|
+
const ControlWidget = ({ control, value, onChange, }) => {
|
|
67
|
+
const labelStyle = {
|
|
68
|
+
color: 'white',
|
|
69
|
+
fontSize: 11,
|
|
70
|
+
fontFamily: 'monospace',
|
|
71
|
+
display: 'flex',
|
|
72
|
+
flexDirection: 'column',
|
|
73
|
+
gap: 2,
|
|
74
|
+
};
|
|
75
|
+
if (control.kind === 'toggle') {
|
|
76
|
+
return (_jsx("button", { onClick: () => onChange(!value), style: {
|
|
77
|
+
background: value ? '#4ade80' : '#374151',
|
|
78
|
+
color: 'white',
|
|
79
|
+
border: 'none',
|
|
80
|
+
borderRadius: 4,
|
|
81
|
+
padding: '4px 8px',
|
|
82
|
+
cursor: 'pointer',
|
|
83
|
+
fontSize: 12,
|
|
84
|
+
fontFamily: 'monospace',
|
|
85
|
+
transition: 'background 0.2s',
|
|
86
|
+
}, children: control.label ?? (value ? 'On' : 'Off') }));
|
|
87
|
+
}
|
|
88
|
+
if (control.kind === 'slider') {
|
|
89
|
+
return (_jsxs("label", { style: labelStyle, children: [_jsxs("span", { children: [control.label, ": ", value, control.unit ? ` ${control.unit}` : ''] }), _jsx("input", { max: control.max, min: control.min, onChange: (e) => onChange(Number(e.target.value)), onPointerDown: (e) => e.stopPropagation(), step: control.step, type: "range", value: value })] }));
|
|
90
|
+
}
|
|
91
|
+
if (control.kind === 'temperature') {
|
|
92
|
+
return (_jsxs("label", { style: labelStyle, children: [_jsxs("span", { children: [control.label, ": ", value, "\u00B0", control.unit] }), _jsx("input", { max: control.max, min: control.min, onChange: (e) => onChange(Number(e.target.value)), onPointerDown: (e) => e.stopPropagation(), step: 1, type: "range", value: value })] }));
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"item-light-system.d.ts","sourceRoot":"","sources":["../../../src/systems/item-light/item-light-system.tsx"],"names":[],"mappings":"AAwFA,wBAAgB,eAAe,4CA+M9B"}
|