@treasuryspatial/viewer-kit 0.2.45 → 0.2.50

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 (68) hide show
  1. package/dist/autoGenerate.d.ts.map +1 -1
  2. package/dist/autoGenerate.js +7 -1
  3. package/dist/camera.d.ts +1 -0
  4. package/dist/camera.d.ts.map +1 -1
  5. package/dist/camera.js +8 -1
  6. package/dist/engine/ViewerEngine.d.ts +20 -0
  7. package/dist/engine/ViewerEngine.d.ts.map +1 -1
  8. package/dist/engine/ViewerEngine.js +254 -63
  9. package/dist/engine/types.d.ts +69 -3
  10. package/dist/engine/types.d.ts.map +1 -1
  11. package/dist/exports/download.d.ts +6 -0
  12. package/dist/exports/download.d.ts.map +1 -1
  13. package/dist/exports/download.js +142 -0
  14. package/dist/exports/three-export.d.ts +4 -1
  15. package/dist/exports/three-export.d.ts.map +1 -1
  16. package/dist/exports/three-export.js +138 -4
  17. package/dist/index.d.ts +13 -9
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +5 -4
  20. package/dist/materials/architectural.d.ts +3 -3
  21. package/dist/materials/architectural.d.ts.map +1 -1
  22. package/dist/materials/architectural.js +295 -37
  23. package/dist/materials/catalogue-data.d.ts +15 -0
  24. package/dist/materials/catalogue-data.d.ts.map +1 -1
  25. package/dist/materials/catalogue-data.js +16 -0
  26. package/dist/materials/presets.d.ts +15 -8
  27. package/dist/materials/presets.d.ts.map +1 -1
  28. package/dist/materials/presets.js +140 -39
  29. package/dist/materials/resolve.d.ts +42 -0
  30. package/dist/materials/resolve.d.ts.map +1 -1
  31. package/dist/materials/resolve.js +228 -13
  32. package/dist/materials/types.d.ts +32 -3
  33. package/dist/materials/types.d.ts.map +1 -1
  34. package/dist/presets/defaults.d.ts.map +1 -1
  35. package/dist/presets/defaults.js +17 -1
  36. package/dist/presets/sciencePresets.d.ts.map +1 -1
  37. package/dist/presets/sciencePresets.js +167 -50
  38. package/dist/scene.d.ts +28 -4
  39. package/dist/scene.d.ts.map +1 -1
  40. package/dist/scene.js +196 -31
  41. package/dist/sceneSemanticRegistry.d.ts +64 -0
  42. package/dist/sceneSemanticRegistry.d.ts.map +1 -0
  43. package/dist/sceneSemanticRegistry.js +199 -0
  44. package/dist/sky/scienceSky.d.ts.map +1 -1
  45. package/dist/sky/scienceSky.js +16 -0
  46. package/dist/systems/debugSystem.d.ts +24 -1
  47. package/dist/systems/debugSystem.d.ts.map +1 -1
  48. package/dist/systems/debugSystem.js +324 -77
  49. package/dist/systems/environmentSystem.d.ts +5 -4
  50. package/dist/systems/environmentSystem.d.ts.map +1 -1
  51. package/dist/systems/environmentSystem.js +138 -62
  52. package/dist/systems/lightingSystem.d.ts +10 -1
  53. package/dist/systems/lightingSystem.d.ts.map +1 -1
  54. package/dist/systems/lightingSystem.js +118 -17
  55. package/dist/systems/postfxSystem.d.ts +5 -1
  56. package/dist/systems/postfxSystem.d.ts.map +1 -1
  57. package/dist/systems/postfxSystem.js +84 -1
  58. package/dist/systems/rendererSystem.d.ts.map +1 -1
  59. package/dist/systems/rendererSystem.js +1 -0
  60. package/dist/tsconfig.tsbuildinfo +1 -1
  61. package/dist/uploads/geometry3dm.d.ts.map +1 -1
  62. package/dist/uploads/geometry3dm.js +1 -0
  63. package/dist/uploads/grasshopper.d.ts.map +1 -1
  64. package/dist/uploads/grasshopper.js +63 -5
  65. package/dist/uploads/mesh.js +5 -5
  66. package/dist/uploads/types.d.ts +4 -0
  67. package/dist/uploads/types.d.ts.map +1 -1
  68. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"autoGenerate.d.ts","sourceRoot":"","sources":["../src/autoGenerate.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,uBAAuB,GAAG;IACpC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,kBAAkB,EAAE,CAAC,OAAO,EAAE,uBAAuB,KAAK,OAAO,CAAC;IAClE,aAAa,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD,KAAK,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACxC,YAAY,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,KAAK,OAAO,CAAC;CACnD,CAAC;AAEF,eAAO,MAAM,sBAAsB,QAAO,gBA6CzC,CAAC"}
1
+ {"version":3,"file":"autoGenerate.d.ts","sourceRoot":"","sources":["../src/autoGenerate.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,uBAAuB,GAAG;IACpC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,kBAAkB,EAAE,CAAC,OAAO,EAAE,uBAAuB,KAAK,OAAO,CAAC;IAClE,aAAa,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD,KAAK,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACxC,YAAY,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,KAAK,OAAO,CAAC;CACnD,CAAC;AAEF,eAAO,MAAM,sBAAsB,QAAO,gBAkDzC,CAAC"}
@@ -1,5 +1,6 @@
1
1
  export const createAutoGenerateGate = () => {
2
2
  const generated = new Set();
3
+ const pending = new Set();
3
4
  const normalizeId = (toolId) => (toolId ?? '').trim();
4
5
  return {
5
6
  shouldAutoGenerate: ({ toolId, toolsStatus, loading, isGhTool, isMeshTool, isUploadSource, enabled = true, }) => {
@@ -18,21 +19,26 @@ export const createAutoGenerateGate = () => {
18
19
  return false;
19
20
  if (generated.has(resolvedId))
20
21
  return false;
21
- generated.add(resolvedId);
22
+ if (pending.has(resolvedId))
23
+ return false;
24
+ pending.add(resolvedId);
22
25
  return true;
23
26
  },
24
27
  markGenerated: (toolId) => {
25
28
  const resolvedId = normalizeId(toolId);
26
29
  if (!resolvedId)
27
30
  return;
31
+ pending.delete(resolvedId);
28
32
  generated.add(resolvedId);
29
33
  },
30
34
  reset: (toolId) => {
31
35
  const resolvedId = normalizeId(toolId);
32
36
  if (!resolvedId) {
37
+ pending.clear();
33
38
  generated.clear();
34
39
  return;
35
40
  }
41
+ pending.delete(resolvedId);
36
42
  generated.delete(resolvedId);
37
43
  },
38
44
  hasGenerated: (toolId) => {
package/dist/camera.d.ts CHANGED
@@ -8,6 +8,7 @@ export type CameraFrameConfig = {
8
8
  export type CameraNormalizationConfig = {
9
9
  recenter?: boolean;
10
10
  floorToZero?: boolean;
11
+ anchor?: THREE.Vector3;
11
12
  normalizePlan?: {
12
13
  target?: number;
13
14
  maxScale?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"camera.d.ts","sourceRoot":"","sources":["../src/camera.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,UAAU,CAAC;AAEjD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC;IACnB,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC;IACtB,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC;CACvB,CAAC;AAKF,eAAO,MAAM,iBAAiB,GAC5B,QAAQ,KAAK,CAAC,QAAQ,EACtB,SAAS,yBAAyB,KACjC,yBAuDF,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,KAAK,CAAC,IAAI,EAClB,YAAY,MAAM,EAClB,SAAS,iBAAiB,KACzB;IAAE,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC;IAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAA;CAoBlD,CAAC"}
1
+ {"version":3,"file":"camera.d.ts","sourceRoot":"","sources":["../src/camera.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,UAAU,CAAC;AAEjD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC;IACnB,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC;IACtB,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC;CACvB,CAAC;AAKF,eAAO,MAAM,iBAAiB,GAC5B,QAAQ,KAAK,CAAC,QAAQ,EACtB,SAAS,yBAAyB,KACjC,yBA+DF,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,KAAK,CAAC,IAAI,EAClB,YAAY,MAAM,EAClB,SAAS,iBAAiB,KACzB;IAAE,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC;IAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAA;CAoBlD,CAAC"}
package/dist/camera.js CHANGED
@@ -49,6 +49,13 @@ export const normalizeObject3d = (object, config) => {
49
49
  bounds.getSize(size);
50
50
  bounds.getCenter(center);
51
51
  }
52
+ if (resolved.anchor) {
53
+ object.position.add(resolved.anchor);
54
+ object.updateMatrixWorld(true);
55
+ bounds = new THREE.Box3().setFromObject(object);
56
+ bounds.getSize(size);
57
+ bounds.getCenter(center);
58
+ }
52
59
  return { bounds, center, size, scale, offset };
53
60
  };
54
61
  export const computeCameraFrame = (bounds, fovDegrees, config) => {
@@ -64,7 +71,7 @@ export const computeCameraFrame = (bounds, fovDegrees, config) => {
64
71
  const padding = config?.padding ?? DEFAULT_FRAME_PADDING;
65
72
  const direction = new THREE.Vector3(...(config?.direction ?? DEFAULT_FRAME_DIRECTION)).normalize();
66
73
  const sphere = bounds.getBoundingSphere(new THREE.Sphere());
67
- const radius = Math.max(0.5, sphere.radius);
74
+ const radius = Math.max(0.05, sphere.radius);
68
75
  const vFov = THREE.MathUtils.degToRad(fovDegrees);
69
76
  const distance = (radius / Math.sin(vFov / 2)) * padding;
70
77
  const position = center.clone().add(direction.multiplyScalar(distance));
@@ -1,13 +1,23 @@
1
1
  import type { CameraState, RenderPresetDefinition, ViewerCreateOptions, ViewerHandle, ViewerSnapshot } from "./types";
2
2
  export declare class ViewerEngine {
3
3
  private static readonly RUNTIME_PRESET_ID;
4
+ private static readonly DEFAULT_NEAR_SAFETY;
5
+ private static readonly DEFAULT_FAR_SAFETY;
6
+ private static readonly DEFAULT_MINIMUM_FAR;
7
+ private static readonly DEFAULT_CONTROL_MAX_DISTANCE_FACTOR;
8
+ private static readonly DEFAULT_CONTROL_MIN_DISTANCE_FACTOR;
4
9
  private container;
5
10
  private renderer;
6
11
  private scene;
7
12
  private camera;
13
+ private perspectiveCamera;
14
+ private orthographicCamera;
15
+ private cameraKind;
16
+ private orthographicHeight;
8
17
  private controls;
9
18
  private rootGroup;
10
19
  private sceneBounds;
20
+ private sceneSphere;
11
21
  private environmentSystem;
12
22
  private lightingSystem;
13
23
  private postFxSystem;
@@ -21,7 +31,15 @@ export declare class ViewerEngine {
21
31
  private lastHeight;
22
32
  private maxPixelRatio;
23
33
  private hasDominantGroundPlane;
34
+ private resolveDebugPreset;
35
+ private applySceneSystems;
36
+ private disposeObject3d;
24
37
  constructor(options: ViewerCreateOptions);
38
+ private syncOrthographicFrustum;
39
+ private switchCamera;
40
+ private updateCameraClipping;
41
+ private refreshSceneClipping;
42
+ private refreshControlLimits;
25
43
  getHandle(): ViewerHandle;
26
44
  private invalidate;
27
45
  start(): void;
@@ -34,6 +52,8 @@ export declare class ViewerEngine {
34
52
  setCamera(state: Partial<CameraState>): void;
35
53
  getCameraState(): CameraState;
36
54
  capturePng(): Promise<Blob>;
55
+ private captureVisibleCanvas;
56
+ private captureOffscreenRender;
37
57
  getSnapshot(): ViewerSnapshot;
38
58
  private getDebugInfo;
39
59
  private applyPreset;
@@ -1 +1 @@
1
- {"version":3,"file":"ViewerEngine.d.ts","sourceRoot":"","sources":["../../src/engine/ViewerEngine.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAiB,WAAW,EAAiB,sBAAsB,EAAE,mBAAmB,EAAmB,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAQrK,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAiB;IAE1D,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,WAAW,CAA2B;IAE9C,OAAO,CAAC,iBAAiB,CAAoB;IAC7C,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,iBAAiB,CAAU;IAEnC,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,aAAa,CAAS;IAE9B,OAAO,CAAC,sBAAsB;gBAiClB,OAAO,EAAE,mBAAmB;IAwDxC,SAAS,IAAI,YAAY;IA0BzB,OAAO,CAAC,UAAU;IAKlB,KAAK,IAAI,IAAI;IAiBb,IAAI,IAAI,IAAI;IAQZ,MAAM,IAAI,IAAI;IAcd,OAAO,IAAI,IAAI;IAef,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAIjC,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,GAAG,IAAI;IAStD,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IA8DjC,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI;IAuB5C,cAAc,IAAI,WAAW;IAUvB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAmCjC,WAAW,IAAI,cAAc;IAe7B,OAAO,CAAC,YAAY;IAuCpB,OAAO,CAAC,WAAW;IAyBnB,OAAO,CAAC,aAAa;CAKtB"}
1
+ {"version":3,"file":"ViewerEngine.d.ts","sourceRoot":"","sources":["../../src/engine/ViewerEngine.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAGV,WAAW,EAEX,sBAAsB,EACtB,mBAAmB,EAEnB,YAAY,EACZ,cAAc,EACf,MAAM,SAAS,CAAC;AAQjB,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAiB;IAC1D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAO;IAClD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAQ;IAClD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAO;IAClD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mCAAmC,CAAM;IACjE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mCAAmC,CAAS;IAEpE,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,iBAAiB,CAA0B;IACnD,OAAO,CAAC,kBAAkB,CAA2B;IACrD,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,WAAW,CAA2B;IAC9C,OAAO,CAAC,WAAW,CAA6B;IAEhD,OAAO,CAAC,iBAAiB,CAAoB;IAC7C,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,iBAAiB,CAAU;IAEnC,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,aAAa,CAAS;IAE9B,OAAO,CAAC,sBAAsB;IAiC9B,OAAO,CAAC,kBAAkB;IAW1B,OAAO,CAAC,iBAAiB;IAKzB,OAAO,CAAC,eAAe;gBA0CX,OAAO,EAAE,mBAAmB;IA0ExC,OAAO,CAAC,uBAAuB;IAa/B,OAAO,CAAC,YAAY;IAcpB,OAAO,CAAC,oBAAoB;IAiB5B,OAAO,CAAC,oBAAoB;IAoB5B,OAAO,CAAC,oBAAoB;IAgB5B,SAAS,IAAI,YAAY;IA0BzB,OAAO,CAAC,UAAU;IAKlB,KAAK,IAAI,IAAI;IAmBb,IAAI,IAAI,IAAI;IAQZ,MAAM,IAAI,IAAI;IAed,OAAO,IAAI,IAAI;IAsBf,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAIjC,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,GAAG,IAAI;IAStD,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IAgCjC,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI;IA2B5C,cAAc,IAAI,WAAW;IAkBvB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;YASnB,oBAAoB;YAmCpB,sBAAsB;IAmCpC,WAAW,IAAI,cAAc;IAe7B,OAAO,CAAC,YAAY;IAmDpB,OAAO,CAAC,WAAW;IAwBnB,OAAO,CAAC,aAAa;CAKtB"}
@@ -8,13 +8,23 @@ import { PostFxSystem } from "../systems/postfxSystem";
8
8
  import { DebugSystem } from "../systems/debugSystem";
9
9
  export class ViewerEngine {
10
10
  static RUNTIME_PRESET_ID = "__runtime__";
11
+ static DEFAULT_NEAR_SAFETY = 1.6;
12
+ static DEFAULT_FAR_SAFETY = 1.15;
13
+ static DEFAULT_MINIMUM_FAR = 220;
14
+ static DEFAULT_CONTROL_MAX_DISTANCE_FACTOR = 18;
15
+ static DEFAULT_CONTROL_MIN_DISTANCE_FACTOR = 0.025;
11
16
  container;
12
17
  renderer;
13
18
  scene;
14
19
  camera;
20
+ perspectiveCamera;
21
+ orthographicCamera;
22
+ cameraKind;
23
+ orthographicHeight;
15
24
  controls;
16
25
  rootGroup;
17
26
  sceneBounds = null;
27
+ sceneSphere = null;
18
28
  environmentSystem;
19
29
  lightingSystem;
20
30
  postFxSystem;
@@ -62,6 +72,63 @@ export class ViewerEngine {
62
72
  });
63
73
  return detected;
64
74
  }
75
+ resolveDebugPreset(preset) {
76
+ if (!this.hasDominantGroundPlane(this.rootGroup, this.sceneBounds))
77
+ return preset;
78
+ return {
79
+ ...preset,
80
+ sceneExtras: {
81
+ ...(preset.sceneExtras ?? {}),
82
+ ground: false,
83
+ },
84
+ };
85
+ }
86
+ applySceneSystems(preset) {
87
+ this.lightingSystem.apply(preset, this.sceneBounds);
88
+ this.debugSystem.apply(this.resolveDebugPreset(preset), this.sceneBounds, this.rootGroup);
89
+ }
90
+ disposeObject3d(object) {
91
+ object.traverse((child) => {
92
+ const mesh = child;
93
+ if (mesh.isMesh) {
94
+ const geometry = mesh.geometry;
95
+ geometry?.dispose?.();
96
+ const material = mesh.material;
97
+ if (Array.isArray(material)) {
98
+ material.forEach((mat) => mat.dispose?.());
99
+ }
100
+ else {
101
+ material?.dispose?.();
102
+ }
103
+ return;
104
+ }
105
+ const line = child;
106
+ if (line.isLine) {
107
+ const geometry = line.geometry;
108
+ geometry?.dispose?.();
109
+ const material = line.material;
110
+ if (Array.isArray(material)) {
111
+ material.forEach((mat) => mat.dispose?.());
112
+ }
113
+ else {
114
+ material?.dispose?.();
115
+ }
116
+ return;
117
+ }
118
+ const points = child;
119
+ if (points.isPoints) {
120
+ const geometry = points.geometry;
121
+ geometry?.dispose?.();
122
+ const material = points.material;
123
+ if (Array.isArray(material)) {
124
+ material.forEach((mat) => mat.dispose?.());
125
+ }
126
+ else {
127
+ material?.dispose?.();
128
+ }
129
+ }
130
+ });
131
+ }
65
132
  constructor(options) {
66
133
  this.container = options.container;
67
134
  const basePresets = options.presets ?? DEFAULT_PRESETS;
@@ -70,13 +137,23 @@ export class ViewerEngine {
70
137
  const width = this.container.clientWidth || 1;
71
138
  const height = this.container.clientHeight || 1;
72
139
  this.scene = new THREE.Scene();
73
- this.camera = new THREE.PerspectiveCamera(options.camera?.fov ?? 60, width / height, options.camera?.near ?? 0.1, options.camera?.far ?? 500);
140
+ this.perspectiveCamera = new THREE.PerspectiveCamera(options.camera?.fov ?? 60, width / height, options.camera?.near ?? 0.1, options.camera?.far ?? 500);
141
+ this.orthographicHeight = options.camera?.orthographicHeight ?? 24;
142
+ const aspect = width / height;
143
+ const halfHeight = this.orthographicHeight / 2;
144
+ const halfWidth = halfHeight * aspect;
145
+ this.orthographicCamera = new THREE.OrthographicCamera(-halfWidth, halfWidth, halfHeight, -halfHeight, options.camera?.near ?? 0.1, options.camera?.far ?? 500);
74
146
  const camPos = options.camera?.position ?? [15, 12, 15];
75
- this.camera.position.set(...camPos);
147
+ this.perspectiveCamera.position.set(...camPos);
148
+ this.orthographicCamera.position.set(...camPos);
149
+ this.cameraKind = options.camera?.kind ?? "perspective";
150
+ this.camera = this.cameraKind === "orthographic" ? this.orthographicCamera : this.perspectiveCamera;
151
+ const initialRendererConfig = this.resolvePreset(this.presetId).renderer;
76
152
  const rendererOptions = {
77
- antialias: options.renderer?.antialias ?? true,
78
- alpha: options.renderer?.alpha ?? false,
79
- preserveDrawingBuffer: options.renderer?.preserveDrawingBuffer ?? false,
153
+ antialias: options.renderer?.antialias ?? initialRendererConfig?.antialias ?? true,
154
+ alpha: options.renderer?.alpha ?? initialRendererConfig?.alpha ?? false,
155
+ preserveDrawingBuffer: options.renderer?.preserveDrawingBuffer ?? initialRendererConfig?.preserveDrawingBuffer ?? false,
156
+ powerPreference: options.renderer?.powerPreference ?? initialRendererConfig?.powerPreference ?? "high-performance",
80
157
  };
81
158
  this.renderer = new THREE.WebGLRenderer(rendererOptions);
82
159
  this.renderer.setSize(width, height);
@@ -85,6 +162,7 @@ export class ViewerEngine {
85
162
  this.controls.enableDamping = true;
86
163
  this.controls.dampingFactor = 0.05;
87
164
  this.controls.addEventListener("change", () => {
165
+ this.refreshSceneClipping();
88
166
  if (this.usePostprocessing) {
89
167
  this.postFxSystem.invalidate();
90
168
  }
@@ -105,6 +183,79 @@ export class ViewerEngine {
105
183
  this.postFxSystem.resize(width, height);
106
184
  this.applyPreset(this.presetId);
107
185
  }
186
+ syncOrthographicFrustum(height) {
187
+ const safeHeight = Number.isFinite(height) && height > 0 ? height : this.orthographicHeight || 24;
188
+ this.orthographicHeight = safeHeight;
189
+ const aspect = this.lastWidth / Math.max(1, this.lastHeight);
190
+ const halfHeight = safeHeight / 2;
191
+ const halfWidth = halfHeight * aspect;
192
+ this.orthographicCamera.left = -halfWidth;
193
+ this.orthographicCamera.right = halfWidth;
194
+ this.orthographicCamera.top = halfHeight;
195
+ this.orthographicCamera.bottom = -halfHeight;
196
+ this.orthographicCamera.updateProjectionMatrix();
197
+ }
198
+ switchCamera(kind) {
199
+ if (this.cameraKind === kind)
200
+ return;
201
+ const currentTarget = this.controls.target.clone();
202
+ const currentPosition = this.camera.position.clone();
203
+ this.cameraKind = kind;
204
+ this.camera = kind === "orthographic" ? this.orthographicCamera : this.perspectiveCamera;
205
+ this.camera.position.copy(currentPosition);
206
+ this.controls.object = this.camera;
207
+ this.controls.target.copy(currentTarget);
208
+ this.controls.update();
209
+ this.postFxSystem.setCamera(this.camera);
210
+ this.environmentSystem.update(this.camera);
211
+ }
212
+ updateCameraClipping(camera, near, far) {
213
+ if (camera instanceof THREE.PerspectiveCamera || camera instanceof THREE.OrthographicCamera) {
214
+ let needsUpdate = false;
215
+ if (typeof near === "number" && Number.isFinite(near) && camera.near !== near) {
216
+ camera.near = near;
217
+ needsUpdate = true;
218
+ }
219
+ if (typeof far === "number" && Number.isFinite(far) && camera.far !== far) {
220
+ camera.far = far;
221
+ needsUpdate = true;
222
+ }
223
+ if (needsUpdate) {
224
+ camera.updateProjectionMatrix();
225
+ }
226
+ }
227
+ }
228
+ refreshSceneClipping() {
229
+ if (!this.sceneSphere || !Number.isFinite(this.sceneSphere.radius) || this.sceneSphere.radius <= 0)
230
+ return;
231
+ const distanceToCenter = this.camera.position.distanceTo(this.sceneSphere.center);
232
+ if (!Number.isFinite(distanceToCenter) || distanceToCenter <= 0)
233
+ return;
234
+ let near = Math.max(0.005, distanceToCenter - this.sceneSphere.radius * ViewerEngine.DEFAULT_NEAR_SAFETY);
235
+ const maxNear = distanceToCenter * 0.08;
236
+ if (Number.isFinite(maxNear) && maxNear > 0) {
237
+ near = Math.min(near, maxNear);
238
+ }
239
+ const dynamicMinimumFar = Math.max(1, distanceToCenter * 1.4, this.sceneSphere.radius * 5);
240
+ const minimumFar = Math.min(ViewerEngine.DEFAULT_MINIMUM_FAR, dynamicMinimumFar);
241
+ const far = Math.max(distanceToCenter + this.sceneSphere.radius * ViewerEngine.DEFAULT_FAR_SAFETY, near + this.sceneSphere.radius * 2, minimumFar);
242
+ this.updateCameraClipping(this.perspectiveCamera, near, far);
243
+ this.updateCameraClipping(this.orthographicCamera, near, far);
244
+ }
245
+ refreshControlLimits() {
246
+ if (!this.sceneSphere || !Number.isFinite(this.sceneSphere.radius) || this.sceneSphere.radius <= 0) {
247
+ this.controls.minDistance = 0.05;
248
+ this.controls.maxDistance = Infinity;
249
+ this.controls.minZoom = 0.01;
250
+ this.controls.maxZoom = Infinity;
251
+ return;
252
+ }
253
+ const radius = Math.max(this.sceneSphere.radius, 0.05);
254
+ this.controls.minDistance = Math.max(0.02, radius * ViewerEngine.DEFAULT_CONTROL_MIN_DISTANCE_FACTOR);
255
+ this.controls.maxDistance = Math.max(20, radius * ViewerEngine.DEFAULT_CONTROL_MAX_DISTANCE_FACTOR);
256
+ this.controls.minZoom = 0.05;
257
+ this.controls.maxZoom = 80;
258
+ }
108
259
  getHandle() {
109
260
  return {
110
261
  start: () => this.start(),
@@ -142,6 +293,8 @@ export class ViewerEngine {
142
293
  if (!this.running)
143
294
  return;
144
295
  this.controls.update();
296
+ this.refreshSceneClipping();
297
+ this.debugSystem.update(this.controls.target);
145
298
  this.environmentSystem.update(this.camera);
146
299
  if (this.usePostprocessing) {
147
300
  this.postFxSystem.render();
@@ -167,8 +320,9 @@ export class ViewerEngine {
167
320
  this.lastHeight = height;
168
321
  const deviceRatio = typeof window !== "undefined" ? window.devicePixelRatio : 1;
169
322
  this.renderer.setPixelRatio(Math.min(deviceRatio, this.maxPixelRatio));
170
- this.camera.aspect = width / height;
171
- this.camera.updateProjectionMatrix();
323
+ this.perspectiveCamera.aspect = width / height;
324
+ this.perspectiveCamera.updateProjectionMatrix();
325
+ this.syncOrthographicFrustum(this.orthographicHeight);
172
326
  this.renderer.setSize(width, height);
173
327
  this.postFxSystem.resize(width, height);
174
328
  this.postFxSystem.invalidate();
@@ -179,9 +333,17 @@ export class ViewerEngine {
179
333
  this.lightingSystem.dispose();
180
334
  this.postFxSystem.dispose();
181
335
  this.debugSystem.dispose();
336
+ this.rootGroup.children.forEach((child) => this.disposeObject3d(child));
337
+ this.rootGroup.clear();
182
338
  this.scene.clear();
183
339
  this.controls.dispose();
184
340
  this.renderer.dispose();
341
+ try {
342
+ this.renderer.forceContextLoss();
343
+ }
344
+ catch {
345
+ // Some browser contexts do not expose explicit context-loss cleanup.
346
+ }
185
347
  if (this.renderer.domElement.parentElement === this.container) {
186
348
  this.container.removeChild(this.renderer.domElement);
187
349
  }
@@ -198,99 +360,118 @@ export class ViewerEngine {
198
360
  this.applyPreset(ViewerEngine.RUNTIME_PRESET_ID);
199
361
  }
200
362
  setSceneRoot(root) {
363
+ this.rootGroup.children.forEach((child) => this.disposeObject3d(child));
201
364
  this.rootGroup.clear();
365
+ this.sceneBounds = null;
366
+ this.sceneSphere = null;
202
367
  const object3d = root;
203
368
  if (object3d && object3d.isObject3D) {
204
369
  this.rootGroup.add(object3d);
205
370
  const bounds = new THREE.Box3().setFromObject(object3d);
206
371
  this.sceneBounds = bounds.clone();
207
372
  if (!bounds.isEmpty()) {
208
- const size = new THREE.Vector3();
209
- bounds.getSize(size);
210
- const maxDim = Math.max(size.x, size.y, size.z);
211
- if (Number.isFinite(maxDim) && maxDim > 0) {
212
- const target = this.controls.target.clone();
213
- const distance = this.camera.position.distanceTo(target);
214
- const sphere = new THREE.Sphere();
215
- bounds.getBoundingSphere(sphere);
216
- const safetyFar = 1.15;
217
- const safetyNear = 1.6;
218
- let near = Math.max(0.01, distance - sphere.radius * safetyNear);
219
- const maxNear = distance * 0.1;
220
- if (Number.isFinite(maxNear) && maxNear > 0) {
221
- near = Math.min(near, maxNear);
222
- }
223
- const minSkyFar = 350;
224
- const far = Math.max(distance + sphere.radius * safetyFar, near + sphere.radius * 2, minSkyFar);
225
- let needsUpdate = false;
226
- if (Number.isFinite(near) && this.camera.near !== near) {
227
- this.camera.near = near;
228
- needsUpdate = true;
229
- }
230
- if (Number.isFinite(far) && this.camera.far !== far) {
231
- this.camera.far = far;
232
- needsUpdate = true;
233
- }
234
- if (needsUpdate) {
235
- this.camera.updateProjectionMatrix();
236
- }
373
+ const sphere = new THREE.Sphere();
374
+ bounds.getBoundingSphere(sphere);
375
+ if (Number.isFinite(sphere.radius) && sphere.radius > 0) {
376
+ this.sceneSphere = sphere.clone();
377
+ this.refreshSceneClipping();
237
378
  }
238
379
  }
239
380
  }
240
- // Re-apply scene extras (ground/grid) to match updated bounds.
381
+ this.refreshControlLimits();
382
+ // Re-apply scene-bound systems after a root swap so workplane, shadows, and helpers match current geometry.
241
383
  try {
242
384
  const preset = this.resolvePreset(this.presetId);
243
- const disableGround = this.hasDominantGroundPlane(this.rootGroup, this.sceneBounds);
244
- const debugPreset = disableGround
245
- ? {
246
- ...preset,
247
- sceneExtras: {
248
- ...(preset.sceneExtras ?? {}),
249
- ground: false,
250
- },
251
- }
252
- : preset;
253
- this.debugSystem.apply(debugPreset, this.sceneBounds);
385
+ this.applySceneSystems(preset);
254
386
  }
255
387
  catch {
256
- // ignore debug system failures
388
+ // ignore presentation system failures
257
389
  }
258
390
  this.postFxSystem.invalidate();
259
391
  // Ensure the render loop is running after a scene update.
260
392
  this.start();
261
393
  }
262
394
  setCamera(state) {
395
+ if (state.kind) {
396
+ this.switchCamera(state.kind);
397
+ }
263
398
  if (state.position) {
264
399
  this.camera.position.set(...state.position);
400
+ const secondaryCamera = this.camera === this.perspectiveCamera ? this.orthographicCamera : this.perspectiveCamera;
401
+ secondaryCamera.position.set(...state.position);
265
402
  }
266
403
  if (state.target) {
267
404
  this.controls.target.set(...state.target);
268
405
  }
269
406
  if (typeof state.fov === "number") {
270
- this.camera.fov = state.fov;
271
- this.camera.updateProjectionMatrix();
272
- }
273
- if (typeof state.near === "number" && Number.isFinite(state.near)) {
274
- this.camera.near = state.near;
275
- this.camera.updateProjectionMatrix();
407
+ this.perspectiveCamera.fov = state.fov;
408
+ this.perspectiveCamera.updateProjectionMatrix();
276
409
  }
277
- if (typeof state.far === "number" && Number.isFinite(state.far)) {
278
- this.camera.far = state.far;
279
- this.camera.updateProjectionMatrix();
410
+ if (typeof state.orthographicHeight === "number" && Number.isFinite(state.orthographicHeight)) {
411
+ this.syncOrthographicFrustum(state.orthographicHeight);
280
412
  }
413
+ this.updateCameraClipping(this.perspectiveCamera, state.near, state.far);
414
+ this.updateCameraClipping(this.orthographicCamera, state.near, state.far);
281
415
  this.controls.update();
416
+ this.refreshControlLimits();
417
+ this.refreshSceneClipping();
282
418
  this.postFxSystem.invalidate();
283
419
  }
284
420
  getCameraState() {
285
421
  return {
422
+ kind: this.cameraKind,
286
423
  position: [this.camera.position.x, this.camera.position.y, this.camera.position.z],
287
424
  target: [this.controls.target.x, this.controls.target.y, this.controls.target.z],
288
- fov: this.camera.fov,
289
- near: this.camera.near,
290
- far: this.camera.far,
425
+ fov: this.perspectiveCamera.fov,
426
+ orthographicHeight: this.orthographicHeight,
427
+ near: this.camera instanceof THREE.PerspectiveCamera || this.camera instanceof THREE.OrthographicCamera
428
+ ? this.camera.near
429
+ : undefined,
430
+ far: this.camera instanceof THREE.PerspectiveCamera || this.camera instanceof THREE.OrthographicCamera
431
+ ? this.camera.far
432
+ : undefined,
291
433
  };
292
434
  }
293
435
  async capturePng() {
436
+ const visibleFrame = await this.captureVisibleCanvas();
437
+ if (visibleFrame) {
438
+ return visibleFrame;
439
+ }
440
+ return this.captureOffscreenRender();
441
+ }
442
+ async captureVisibleCanvas() {
443
+ const canvas = this.renderer.domElement;
444
+ if (!canvas || canvas.width < 1 || canvas.height < 1) {
445
+ return null;
446
+ }
447
+ this.controls.update();
448
+ this.debugSystem.update(this.controls.target);
449
+ this.environmentSystem.update(this.camera);
450
+ if (this.usePostprocessing) {
451
+ this.postFxSystem.render();
452
+ }
453
+ else {
454
+ this.renderer.setRenderTarget(null);
455
+ this.renderer.clear(true, true, true);
456
+ this.renderer.render(this.scene, this.camera);
457
+ }
458
+ const gl = this.renderer.getContext();
459
+ gl.finish?.();
460
+ try {
461
+ const dataUrl = canvas.toDataURL("image/png");
462
+ if (!dataUrl.startsWith("data:image/png")) {
463
+ return null;
464
+ }
465
+ const response = await fetch(dataUrl);
466
+ const blob = await response.blob();
467
+ return blob.size > 0 ? blob : null;
468
+ }
469
+ catch (error) {
470
+ console.warn("[viewer-kit] Visible canvas capture failed, falling back to offscreen render.", error);
471
+ return null;
472
+ }
473
+ }
474
+ async captureOffscreenRender() {
294
475
  const width = this.renderer.domElement.width || 1;
295
476
  const height = this.renderer.domElement.height || 1;
296
477
  const target = new THREE.WebGLRenderTarget(width, height);
@@ -340,6 +521,7 @@ export class ViewerEngine {
340
521
  const postfxState = this.postFxSystem.getState();
341
522
  const renderInfo = this.renderer.info.render;
342
523
  const clearColor = new THREE.Color();
524
+ const boundsCenter = this.sceneBounds && !this.sceneBounds.isEmpty() ? this.sceneBounds.getCenter(new THREE.Vector3()) : null;
343
525
  this.renderer.getClearColor(clearColor);
344
526
  return {
345
527
  presetId: this.presetId,
@@ -368,7 +550,17 @@ export class ViewerEngine {
368
550
  children: this.scene.children.length,
369
551
  rootChildren: this.rootGroup.children.length,
370
552
  lights: this.scene.children.filter((child) => child?.isLight).length,
553
+ bounds: this.sceneBounds && !this.sceneBounds.isEmpty() && boundsCenter && this.sceneSphere
554
+ ? {
555
+ min: [this.sceneBounds.min.x, this.sceneBounds.min.y, this.sceneBounds.min.z],
556
+ max: [this.sceneBounds.max.x, this.sceneBounds.max.y, this.sceneBounds.max.z],
557
+ center: [boundsCenter.x, boundsCenter.y, boundsCenter.z],
558
+ radius: this.sceneSphere.radius,
559
+ }
560
+ : null,
371
561
  },
562
+ workplane: this.debugSystem.getState(),
563
+ lighting: this.lightingSystem.getState(),
372
564
  environment: envState,
373
565
  postfx: postfxState,
374
566
  };
@@ -390,10 +582,9 @@ export class ViewerEngine {
390
582
  this.renderer.setSize(this.lastWidth, this.lastHeight);
391
583
  this.postFxSystem.resize(this.lastWidth, this.lastHeight);
392
584
  void this.environmentSystem.apply(preset);
393
- this.lightingSystem.apply(preset);
585
+ this.applySceneSystems(preset);
394
586
  this.postFxSystem.apply(this.usePostprocessing ? preset.postfx : undefined);
395
587
  this.postFxSystem.invalidate();
396
- this.debugSystem.apply(preset, this.sceneBounds);
397
588
  }
398
589
  resolvePreset(presetId) {
399
590
  if (presetId in this.presets)