@geode/opengeodeweb-front 10.34.0-rc.8 → 10.34.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.
Files changed (282) hide show
  1. package/{server → .build/server}/utils/app_config.js +10 -9
  2. package/{server → .build/server}/utils/cleanup.js +31 -17
  3. package/{server → .build/server}/utils/cloud.js +1 -4
  4. package/{server → .build/server}/utils/microservices.js +21 -9
  5. package/{server → .build/server}/utils/path.js +4 -4
  6. package/{server → .build/server}/utils/scripts.js +9 -9
  7. package/.build/server/utils/server_config.js +51 -0
  8. package/.build/server/utils/stream.js +6 -0
  9. package/{server → .build/server}/utils/ws_client.js +18 -3
  10. package/{shared → .build/shared}/scripts.js +10 -10
  11. package/.build/shared/utils/call_raw.js +31 -0
  12. package/{shared → .build/shared}/utils/call_schema.js +2 -2
  13. package/.build/shared/utils/fetch_raw.js +52 -0
  14. package/{shared → .build/shared}/utils/fetch_schema.js +2 -2
  15. package/{shared → .build/shared}/utils/file.js +3 -1
  16. package/{shared → .build/shared}/utils/response_handlers/load.js +10 -7
  17. package/{shared → .build/shared}/utils/validate_schema.js +1 -1
  18. package/app/components/ActionButton.vue +6 -6
  19. package/app/components/CameraManager/List.vue +5 -5
  20. package/app/components/CameraManager/Saver.vue +3 -5
  21. package/app/components/CameraManager.vue +6 -4
  22. package/app/components/CameraOrientation.vue +12 -12
  23. package/app/components/ClippingPlaneCard.vue +4 -2
  24. package/app/components/ClippingPlanes.vue +8 -8
  25. package/app/components/CrsSelector.vue +9 -10
  26. package/app/components/DeleteDialog.vue +4 -2
  27. package/app/components/DragAndDrop.vue +10 -12
  28. package/app/components/DragAndDropInternal/DragAndDropInline.vue +4 -2
  29. package/app/components/ExtensionSelector.vue +4 -4
  30. package/app/components/FeedBack/ErrorBanner.vue +3 -1
  31. package/app/components/FeedBack/Snackers.vue +4 -3
  32. package/app/components/FileSelector.vue +10 -8
  33. package/app/components/FileUploader.vue +11 -11
  34. package/app/components/GlassCard.vue +1 -1
  35. package/app/components/HybridRenderingView.vue +7 -5
  36. package/app/components/HybridViewerTooltip.vue +7 -7
  37. package/app/components/Launcher.vue +1 -1
  38. package/app/components/Loading/EcoMessages.vue +9 -3
  39. package/app/components/Loading.vue +2 -2
  40. package/app/components/MissingFilesSelector.vue +12 -10
  41. package/app/components/ObjectSelector.vue +10 -9
  42. package/app/components/PackagesVersions.vue +6 -5
  43. package/app/components/Recaptcha.vue +3 -3
  44. package/app/components/RemoteRenderingView.vue +6 -7
  45. package/app/components/Ruler.vue +3 -3
  46. package/app/components/Screenshot.vue +6 -6
  47. package/app/components/SearchBar.vue +4 -2
  48. package/app/components/ShrinkFilter.vue +7 -7
  49. package/app/components/Step.vue +6 -5
  50. package/app/components/Stepper.vue +4 -3
  51. package/app/components/ToolPanel.vue +6 -3
  52. package/app/components/ViewToolbar.vue +25 -28
  53. package/app/components/Viewer/ContextMenu/CenterButton.vue +7 -5
  54. package/app/components/Viewer/ContextMenu/CircularItems.vue +3 -3
  55. package/app/components/Viewer/ContextMenu/ContextMenu.vue +22 -19
  56. package/app/components/Viewer/ContextMenu/ContextMenuItem.vue +10 -8
  57. package/app/components/Viewer/ContextMenu/InfoCard.vue +11 -12
  58. package/app/components/Viewer/EdgedCurve/SpecificEdgesOptions.vue +13 -17
  59. package/app/components/Viewer/Generic/Mesh/CellsOptions.vue +11 -15
  60. package/app/components/Viewer/Generic/Mesh/EdgesOptions.vue +12 -16
  61. package/app/components/Viewer/Generic/Mesh/PointsOptions.vue +8 -12
  62. package/app/components/Viewer/Generic/Mesh/PolygonsOptions.vue +11 -15
  63. package/app/components/Viewer/Generic/Mesh/PolyhedraOptions.vue +11 -15
  64. package/app/components/Viewer/Generic/Model/BlocksOptions.vue +67 -38
  65. package/app/components/Viewer/Generic/Model/CornersOptions.vue +53 -23
  66. package/app/components/Viewer/Generic/Model/EdgesOptions.vue +1 -3
  67. package/app/components/Viewer/Generic/Model/LinesOptions.vue +67 -38
  68. package/app/components/Viewer/Generic/Model/ModelStyleCard.vue +31 -15
  69. package/app/components/Viewer/Generic/Model/ModelStyleOptions.vue +2 -3
  70. package/app/components/Viewer/Generic/Model/PointsOptions.vue +6 -6
  71. package/app/components/Viewer/Generic/Model/SurfacesOptions.vue +69 -38
  72. package/app/components/Viewer/ObjectTree/Base/CommonTreeView.vue +64 -46
  73. package/app/components/Viewer/ObjectTree/Base/Controls.vue +5 -5
  74. package/app/components/Viewer/ObjectTree/Base/ItemLabel.vue +13 -9
  75. package/app/components/Viewer/ObjectTree/Base/StickyHeader.vue +5 -5
  76. package/app/components/Viewer/ObjectTree/Base/TreeRow.vue +10 -10
  77. package/app/components/Viewer/ObjectTree/Box.vue +6 -4
  78. package/app/components/Viewer/ObjectTree/Layout.vue +23 -21
  79. package/app/components/Viewer/ObjectTree/Views/GlobalObjects.vue +14 -14
  80. package/app/components/Viewer/ObjectTree/Views/{ModelCollections.vue → ModelTree.vue} +59 -24
  81. package/app/components/Viewer/Options/AttributeColorBar.vue +4 -2
  82. package/app/components/Viewer/Options/AttributeRangeSelector.vue +4 -2
  83. package/app/components/Viewer/Options/AttributeSelector.vue +7 -9
  84. package/app/components/Viewer/Options/ColorMapList.vue +15 -13
  85. package/app/components/Viewer/Options/ColorMapPicker.vue +4 -4
  86. package/app/components/Viewer/Options/ColorPicker.vue +223 -41
  87. package/app/components/Viewer/Options/ColoringTypeSelector.vue +16 -16
  88. package/app/components/Viewer/Options/ColormapQuickPicker.vue +7 -5
  89. package/app/components/Viewer/Options/OptionsSection.vue +1 -1
  90. package/app/components/Viewer/Options/TextureItem.vue +8 -6
  91. package/app/components/Viewer/Options/TexturesSelector.vue +1 -1
  92. package/app/components/Viewer/OverlappingObjectsPicker.vue +8 -9
  93. package/app/components/Viewer/PointSet/SpecificPointsOptions.vue +2 -6
  94. package/app/components/Viewer/Solid/SpecificPolyhedraOptions.vue +12 -16
  95. package/app/components/Viewer/Surface/PolygonsOptions.vue +12 -16
  96. package/app/components/Viewer/Ui.vue +2 -2
  97. package/app/components/Wrapper.vue +1 -4
  98. package/app/components/ZScaling.vue +5 -3
  99. package/app/components/csv-preview/CsvPreviewer.vue +38 -37
  100. package/app/components/csv-preview/CsvSettings.vue +6 -4
  101. package/app/components/csv-preview/CsvTable.vue +1 -1
  102. package/app/composables/batch_style.ts +5 -6
  103. package/app/composables/clipping_planes_widget.ts +73 -181
  104. package/app/composables/clipping_planes_widget_entries.ts +189 -0
  105. package/app/composables/copy_to_clipboard.ts +7 -0
  106. package/app/composables/global_attribute_style.ts +163 -83
  107. package/app/composables/hover_highlight.ts +46 -14
  108. package/app/composables/model_collections.ts +26 -13
  109. package/app/composables/model_components.ts +26 -13
  110. package/app/composables/project_manager.ts +76 -19
  111. package/app/composables/responsive_middle_truncate.ts +13 -13
  112. package/app/composables/stepper_tree.ts +34 -11
  113. package/app/composables/tree_filter.ts +66 -44
  114. package/app/composables/tree_keyboard_nav.ts +25 -11
  115. package/app/composables/tree_scroll.ts +44 -14
  116. package/app/composables/use_adaptive_styles.ts +66 -23
  117. package/app/composables/use_overlapping_picker.ts +66 -16
  118. package/app/composables/use_quick_colormap.ts +16 -5
  119. package/app/composables/virtual_tree.ts +75 -43
  120. package/app/composables/vtk_implicit_plane_widget.d.ts +2 -0
  121. package/app/plugins/auto_store_register.ts +2 -7
  122. package/app/plugins/click_outside_ignore_snackbars.client.ts +39 -0
  123. package/app/stores/app.ts +57 -192
  124. package/app/stores/app_helpers/extension.ts +179 -0
  125. package/app/stores/back.ts +37 -21
  126. package/app/stores/camera_manager.ts +22 -20
  127. package/app/stores/cloud.ts +17 -13
  128. package/app/stores/data.ts +88 -47
  129. package/app/stores/data_helpers/collections.ts +36 -38
  130. package/app/stores/data_helpers/mesh.ts +45 -36
  131. package/app/stores/data_style.ts +50 -28
  132. package/app/stores/feedback.ts +6 -6
  133. package/app/stores/hybrid_viewer.ts +41 -52
  134. package/app/stores/infra.ts +39 -37
  135. package/app/stores/menu.ts +23 -14
  136. package/app/stores/treeview.ts +72 -42
  137. package/app/stores/viewer.ts +39 -27
  138. package/app/utils/attributes.ts +3 -3
  139. package/app/utils/clipping_planes.ts +111 -14
  140. package/app/utils/color_picker.ts +57 -0
  141. package/app/utils/colormap.ts +58 -35
  142. package/app/utils/default_styles/index.ts +2 -2
  143. package/app/utils/default_styles/meshes.ts +107 -13
  144. package/app/utils/default_styles/models.ts +83 -14
  145. package/app/utils/extension.ts +47 -20
  146. package/app/utils/import_workflow.ts +21 -13
  147. package/app/utils/name_cleaner.ts +1 -1
  148. package/app/utils/string.ts +1 -1
  149. package/app/utils/treeview.ts +2 -2
  150. package/internal/database/base_database.ts +7 -3
  151. package/internal/database/database.ts +36 -26
  152. package/internal/database/extended_database.ts +1 -1
  153. package/internal/stores/data_style/mesh/cells/cell.ts +70 -34
  154. package/internal/stores/data_style/mesh/cells/color.ts +12 -6
  155. package/internal/stores/data_style/mesh/cells/common.ts +18 -8
  156. package/internal/stores/data_style/mesh/cells/index.ts +122 -42
  157. package/internal/stores/data_style/mesh/cells/textures.ts +12 -6
  158. package/internal/stores/data_style/mesh/cells/vertex.ts +75 -34
  159. package/internal/stores/data_style/mesh/cells/visibility.ts +15 -4
  160. package/internal/stores/data_style/mesh/edges/color.ts +12 -6
  161. package/internal/stores/data_style/mesh/edges/common.ts +14 -6
  162. package/internal/stores/data_style/mesh/edges/edge.ts +71 -39
  163. package/internal/stores/data_style/mesh/edges/index.ts +117 -42
  164. package/internal/stores/data_style/mesh/edges/vertex.ts +76 -39
  165. package/internal/stores/data_style/mesh/edges/visibility.ts +15 -4
  166. package/internal/stores/data_style/mesh/edges/width.ts +12 -4
  167. package/internal/stores/data_style/mesh/index.ts +30 -11
  168. package/internal/stores/data_style/mesh/points/color.ts +12 -6
  169. package/internal/stores/data_style/mesh/points/common.ts +21 -8
  170. package/internal/stores/data_style/mesh/points/index.ts +101 -50
  171. package/internal/stores/data_style/mesh/points/size.ts +12 -4
  172. package/internal/stores/data_style/mesh/points/vertex.ts +75 -34
  173. package/internal/stores/data_style/mesh/points/visibility.ts +17 -7
  174. package/internal/stores/data_style/mesh/polygons/color.ts +12 -6
  175. package/internal/stores/data_style/mesh/polygons/common.ts +23 -6
  176. package/internal/stores/data_style/mesh/polygons/index.ts +130 -70
  177. package/internal/stores/data_style/mesh/polygons/polygon.ts +77 -43
  178. package/internal/stores/data_style/mesh/polygons/textures.ts +12 -6
  179. package/internal/stores/data_style/mesh/polygons/vertex.ts +79 -39
  180. package/internal/stores/data_style/mesh/polygons/visibility.ts +16 -6
  181. package/internal/stores/data_style/mesh/polyhedra/color.ts +12 -6
  182. package/internal/stores/data_style/mesh/polyhedra/common.ts +14 -6
  183. package/internal/stores/data_style/mesh/polyhedra/index.ts +115 -56
  184. package/internal/stores/data_style/mesh/polyhedra/polyhedron.ts +80 -39
  185. package/internal/stores/data_style/mesh/polyhedra/vertex.ts +79 -39
  186. package/internal/stores/data_style/mesh/polyhedra/visibility.ts +16 -6
  187. package/internal/stores/data_style/model/blocks/color.ts +43 -13
  188. package/internal/stores/data_style/model/blocks/common.ts +21 -6
  189. package/internal/stores/data_style/model/blocks/index.ts +89 -59
  190. package/internal/stores/data_style/model/blocks/polyhedron.ts +101 -44
  191. package/internal/stores/data_style/model/blocks/vertex.ts +107 -42
  192. package/internal/stores/data_style/model/blocks/visibility.ts +17 -4
  193. package/internal/stores/data_style/model/color.ts +171 -83
  194. package/internal/stores/data_style/model/common.ts +70 -21
  195. package/internal/stores/data_style/model/corners/color.ts +43 -11
  196. package/internal/stores/data_style/model/corners/common.ts +25 -6
  197. package/internal/stores/data_style/model/corners/index.ts +66 -41
  198. package/internal/stores/data_style/model/corners/vertex.ts +105 -74
  199. package/internal/stores/data_style/model/corners/visibility.ts +19 -4
  200. package/internal/stores/data_style/model/edges/common.ts +12 -4
  201. package/internal/stores/data_style/model/edges/index.ts +2 -1
  202. package/internal/stores/data_style/model/edges/visibility.ts +24 -7
  203. package/internal/stores/data_style/model/index.ts +24 -11
  204. package/internal/stores/data_style/model/lines/color.ts +41 -11
  205. package/internal/stores/data_style/model/lines/common.ts +17 -6
  206. package/internal/stores/data_style/model/lines/edge.ts +105 -42
  207. package/internal/stores/data_style/model/lines/index.ts +86 -59
  208. package/internal/stores/data_style/model/lines/vertex.ts +105 -42
  209. package/internal/stores/data_style/model/lines/visibility.ts +17 -4
  210. package/internal/stores/data_style/model/points/common.ts +8 -4
  211. package/internal/stores/data_style/model/points/index.ts +10 -3
  212. package/internal/stores/data_style/model/points/size.ts +12 -4
  213. package/internal/stores/data_style/model/points/visibility.ts +16 -6
  214. package/internal/stores/data_style/model/selection.ts +80 -58
  215. package/internal/stores/data_style/model/surfaces/color.ts +41 -11
  216. package/internal/stores/data_style/model/surfaces/common.ts +23 -6
  217. package/internal/stores/data_style/model/surfaces/index.ts +89 -59
  218. package/internal/stores/data_style/model/surfaces/polygon.ts +107 -67
  219. package/internal/stores/data_style/model/surfaces/vertex.ts +111 -67
  220. package/internal/stores/data_style/model/surfaces/visibility.ts +18 -5
  221. package/internal/stores/data_style/model/visibility.ts +82 -20
  222. package/internal/stores/data_style/state.ts +77 -65
  223. package/internal/stores/hybrid_viewer/brightness.ts +49 -31
  224. package/internal/stores/hybrid_viewer/camera.ts +191 -177
  225. package/internal/stores/hybrid_viewer/camera_animation.ts +11 -10
  226. package/internal/stores/hybrid_viewer/core.ts +64 -0
  227. package/internal/stores/hybrid_viewer/filters.ts +10 -16
  228. package/internal/stores/hybrid_viewer/highlight.ts +91 -130
  229. package/internal/stores/hybrid_viewer/ruler.ts +24 -15
  230. package/internal/stores/hybrid_viewer/scene.ts +96 -117
  231. package/internal/stores/hybrid_viewer/viewport.ts +74 -65
  232. package/internal/stores/hybrid_viewer/vtk_types.ts +13 -29
  233. package/internal/utils/api_fetch.ts +54 -23
  234. package/internal/utils/upload_file.ts +74 -53
  235. package/internal/utils/viewer_call.ts +22 -13
  236. package/internal/utils/ws_client.ts +24 -13
  237. package/nuxt.config.ts +58 -1
  238. package/package.json +20 -8
  239. package/server/api/cloud/extensions/run.post.ts +6 -10
  240. package/server/api/local/app/project_folder_path.post.ts +4 -4
  241. package/server/api/local/app/run_back.post.ts +5 -7
  242. package/server/api/local/app/run_viewer.post.ts +5 -7
  243. package/server/api/local/extensions/kill.post.ts +4 -3
  244. package/server/api/local/extensions/run.post.ts +7 -10
  245. package/server/api/local/extensions/upload.put.ts +44 -54
  246. package/server/api/microservice/app/get_is_app_ready.get.ts +4 -5
  247. package/server/api/microservice/app/set_app_base_url.post.ts +5 -6
  248. package/server/api/microservice/app/set_back_base_url.post.ts +5 -6
  249. package/server/api/microservice/app/set_is_app_ready.post.ts +5 -6
  250. package/server/api/microservice/app/set_viewer_base_url.post.ts +5 -6
  251. package/server/api/microservice/extensions/download.post.ts +5 -4
  252. package/server/api/serverless/run_cloud.post.ts +53 -11
  253. package/server/utils/app_config.ts +29 -22
  254. package/server/utils/cleanup.ts +60 -30
  255. package/server/utils/cloud.ts +9 -8
  256. package/server/utils/extension.ts +23 -9
  257. package/server/utils/microservices.ts +33 -24
  258. package/server/utils/path.ts +4 -4
  259. package/server/utils/scripts.ts +26 -23
  260. package/server/utils/server.ts +2 -2
  261. package/server/utils/server_config.ts +41 -27
  262. package/server/utils/stream.ts +6 -0
  263. package/server/utils/ws_client.ts +42 -8
  264. package/shared/scripts.ts +15 -10
  265. package/shared/utils/call_raw.ts +23 -16
  266. package/shared/utils/call_schema.ts +5 -8
  267. package/shared/utils/fetch_raw.ts +87 -47
  268. package/shared/utils/fetch_schema.ts +6 -9
  269. package/shared/utils/file.ts +8 -2
  270. package/shared/utils/response_handlers/load.ts +26 -11
  271. package/shared/utils/types.ts +9 -9
  272. package/shared/utils/validate_schema.ts +1 -1
  273. package/app/components/Viewer/ObjectTree/Views/ModelComponents.vue +0 -276
  274. package/server/types/vendor.d.ts +0 -64
  275. package/server/utils/server_config.js +0 -43
  276. package/shared/utils/call_raw.js +0 -30
  277. package/shared/utils/fetch_raw.js +0 -40
  278. /package/{opengeodeweb_front_schemas.js → .build/opengeodeweb_front_schemas.js} +0 -0
  279. /package/{server → .build/server}/utils/server.js +0 -0
  280. /package/{shared → .build/shared}/app_mode.js +0 -0
  281. /package/{shared → .build/shared}/utils/parse_boolean.js +0 -0
  282. /package/{shared → .build/shared}/utils/types.js +0 -0
@@ -1,17 +1,15 @@
1
- // Not auto-fixable (eslint's sort-imports core rule has no autofixer) and this file's import order doesn't match its syntax-kind-then-alphabetical requirement - left as-is rather than manually reordered across the codebase for a purely cosmetic rule.
2
- // oxlint-disable eslint/sort-imports
1
+ import type { CameraOptions, Vector3, vtkCamera, vtkOpenGLRenderWindow } from "./vtk_types";
3
2
  import {
4
3
  LONG_ANIMATION_DURATION,
5
4
  SHORT_ANIMATION_DURATION,
6
5
  animateCamera,
7
6
  computeAnimationDuration,
8
7
  } from "./camera_animation";
8
+ import { requireRenderWindow, useHybridViewerCore } from "./core";
9
9
  import { dot } from "@kitware/vtk.js/Common/Core/Math";
10
- import { useHybridViewerStore } from "@ogw_front/stores/hybrid_viewer";
10
+ import { useHybridViewerScene } from "./scene";
11
11
  import { useViewerStore } from "@ogw_front/stores/viewer";
12
12
  import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json";
13
- import type { Ref } from "vue";
14
- import type { CameraOptions, HybridViewerStorePublic, Vector3, vtkCamera } from "./vtk_types";
15
13
 
16
14
  const BUMP_MULTIPLIER = 0.2;
17
15
  const ALIGNMENT_THRESHOLD = 0.9;
@@ -49,26 +47,20 @@ const ORIENTATIONS: Record<string, OrientationConfig> = {
49
47
  },
50
48
  };
51
49
 
52
- // `genericRenderWindow.value` and its vtk objects are only ever read from these functions after the viewer has been initialized (see app/stores/hybrid_viewer.ts's initHybridViewer), so the non-null assertions below reflect that existing invariant rather than a new assumption.
53
-
54
- function getImageStyle(): unknown {
55
- const { genericRenderWindow } = useHybridViewerStore() as unknown as HybridViewerStorePublic;
50
+ function getImageStyle(): CSSStyleDeclaration | undefined {
51
+ const { genericRenderWindow } = useHybridViewerCore();
56
52
  if (!genericRenderWindow.value) {
57
53
  return undefined;
58
54
  }
59
- const webGLRenderWindow = genericRenderWindow.value.getApiSpecificRenderWindow();
60
- if (!webGLRenderWindow) {
61
- return undefined;
62
- }
63
- const bgImage = webGLRenderWindow.getReferenceByName("bgImage");
64
- return bgImage ? bgImage.style : undefined;
55
+ // oxlint-disable-next-line no-unsafe-assignment -- vtk.js has no types for getApiSpecificRenderWindow(); narrowed below.
56
+ const apiSpecificRenderWindow = genericRenderWindow.value.getApiSpecificRenderWindow();
57
+ // oxlint-disable-next-line no-unsafe-type-assertion -- trusted vtk.js OpenGL render window API boundary.
58
+ const webGLRenderWindow = apiSpecificRenderWindow as unknown as vtkOpenGLRenderWindow;
59
+ const bgImage: unknown = webGLRenderWindow.getReferenceByName("bgImage");
60
+ return bgImage instanceof HTMLImageElement ? bgImage.style : undefined;
65
61
  }
66
62
 
67
- function getCameraOptions(camera: vtkCamera | undefined): CameraOptions | undefined {
68
- if (!camera || !camera.getFocalPoint) {
69
- // Dead branch in practice (a real vtkCamera always has getFocalPoint); kept for parity with the original defensive check.
70
- return camera as CameraOptions | undefined;
71
- }
63
+ function getCameraOptions(camera: vtkCamera): CameraOptions {
72
64
  return {
73
65
  focal_point: [...(camera.getFocalPoint() ?? [0, 0, 0])] as Vector3,
74
66
  view_up: [...(camera.getViewUp() ?? [0, 0, 0])] as Vector3,
@@ -79,38 +71,11 @@ function getCameraOptions(camera: vtkCamera | undefined): CameraOptions | undefi
79
71
  };
80
72
  }
81
73
 
82
- function performSyncRemoteCamera(): void {
83
- const { genericRenderWindow, camera_options, remoteRender } =
84
- useHybridViewerStore() as unknown as HybridViewerStorePublic;
85
- const viewerStore = useViewerStore();
86
- const renderer = genericRenderWindow.value!.getRenderer();
87
- const camera = renderer.getActiveCamera();
88
- const options_camera = getCameraOptions(camera);
89
- const schema = viewer_schemas.opengeodeweb_viewer.viewer.update_camera;
90
- const params = {
91
- camera_options: options_camera,
92
- };
93
- viewerStore.request(
94
- {
95
- schema,
96
- params,
97
- },
98
- {
99
- response_function: () => {
100
- remoteRender();
101
- if (camera_options) {
102
- Object.assign(camera_options, options_camera);
103
- }
104
- },
105
- },
106
- );
107
- }
108
-
109
74
  function applyCameraOptions(
110
75
  camera: vtkCamera | undefined,
111
- options: CameraOptions | undefined,
76
+ options: Partial<CameraOptions> | undefined,
112
77
  ): void {
113
- if (camera && camera.set && options) {
78
+ if (camera && options) {
114
79
  camera.set({
115
80
  focalPoint: options.focal_point,
116
81
  viewUp: options.view_up,
@@ -121,136 +86,206 @@ function applyCameraOptions(
121
86
  }
122
87
  }
123
88
 
124
- function performSetCamera(targetCameraOptions: CameraOptions): void {
125
- const hybridViewerStore = useHybridViewerStore();
126
- const { genericRenderWindow } = hybridViewerStore as unknown as HybridViewerStorePublic;
127
- const { is_moving } = storeToRefs(hybridViewerStore) as unknown as { is_moving: Ref<boolean> };
128
- const imageStyle = getImageStyle() as { opacity: number } | undefined;
129
- const renderer = genericRenderWindow.value!.getRenderer();
130
- const camera = renderer.getActiveCamera();
131
- const startState = getCameraOptions(camera);
132
- const duration = computeAnimationDuration(startState!, targetCameraOptions);
133
- is_moving.value = true;
134
- if (imageStyle) {
135
- imageStyle.opacity = 0;
136
- }
137
- animateCamera({
138
- camera,
139
- startState: startState!,
140
- targetState: targetCameraOptions,
141
- duration,
142
- bumpMultiplier: 0,
143
- easeExponent: EASE_EXPONENT,
144
- onUpdate: () => {
145
- const renderWindow = genericRenderWindow.value!.getRenderWindow();
146
- renderWindow.render();
147
- },
148
- onEnd: () => {
149
- applyCameraOptions(camera, targetCameraOptions);
150
- const renderWindow = genericRenderWindow.value!.getRenderWindow();
151
- renderWindow.render();
152
- is_moving.value = false;
153
- performSyncRemoteCamera();
154
- },
155
- });
89
+ const BOUNDS_LENGTH = 6;
90
+
91
+ function isBounds(value: unknown): value is [number, number, number, number, number, number] {
92
+ return (
93
+ Array.isArray(value) &&
94
+ value.length === BOUNDS_LENGTH &&
95
+ value.every((item): item is number => typeof item === "number")
96
+ );
156
97
  }
157
98
 
158
- async function performFocusCameraOnObject(id: string, block_ids: string[] = []): Promise<void> {
159
- const { genericRenderWindow, hybridDb } =
160
- useHybridViewerStore() as unknown as HybridViewerStorePublic;
161
- if (!hybridDb[id]) {
162
- return;
99
+ function toBounds(value: unknown): [number, number, number, number, number, number] {
100
+ if (!isBounds(value)) {
101
+ throw new Error("Expected a six-element numeric bounds array");
163
102
  }
164
- const viewerStore = useViewerStore();
165
- let bounds: number[] = [];
166
- if (block_ids.length > 0) {
167
- const schema = viewer_schemas.opengeodeweb_viewer.model.get_blocks_bounds;
168
- const params = {
169
- id,
170
- block_ids,
171
- };
172
- bounds = (await viewerStore.request({
173
- schema,
174
- params,
175
- })) as number[];
176
- } else {
177
- bounds = hybridDb[id].actor.getBounds();
178
- }
179
- const renderer = genericRenderWindow.value!.getRenderer();
180
- const camera = renderer.getActiveCamera();
181
- const startOptions = getCameraOptions(camera);
182
- renderer.resetCamera(bounds as [number, number, number, number, number, number]);
183
- const targetOptions = getCameraOptions(camera);
184
- applyCameraOptions(camera, startOptions);
185
- performSetCamera(targetOptions!);
103
+ return value;
186
104
  }
187
105
 
188
- function performCameraOrientation(orientation: string): void {
189
- const config = ORIENTATIONS[orientation.toLowerCase()];
190
- if (!config) {
106
+ function centerCameraOnPosition(
107
+ camera: vtkCamera | undefined,
108
+ pickedPosition: Vector3 | undefined,
109
+ ): void {
110
+ if (!camera || !pickedPosition) {
191
111
  return;
192
112
  }
193
- const hybridViewerStore = useHybridViewerStore();
194
- const { genericRenderWindow } = hybridViewerStore as unknown as HybridViewerStorePublic;
195
- const { is_moving } = storeToRefs(hybridViewerStore) as unknown as { is_moving: Ref<boolean> };
196
- const imageStyle = getImageStyle() as { opacity: number } | undefined;
197
- const renderer = genericRenderWindow.value!.getRenderer();
198
- const camera = renderer.getActiveCamera();
199
- const startState = getCameraOptions(camera);
200
- applyCameraOptions(camera, {
201
- ...config,
202
- focal_point: [0, 0, 0],
203
- } as CameraOptions);
204
- renderer.resetCamera();
205
- const targetState = getCameraOptions(camera);
206
- applyCameraOptions(camera, startState);
207
- const alignment = dot(camera.getDirectionOfProjection(), config.position);
208
- const duration =
209
- alignment > ALIGNMENT_THRESHOLD ? LONG_ANIMATION_DURATION : SHORT_ANIMATION_DURATION;
210
- is_moving.value = true;
211
- if (imageStyle) {
212
- imageStyle.opacity = 0;
213
- }
214
- animateCamera({
215
- camera,
216
- startState: startState!,
217
- targetState: targetState!,
218
- duration,
219
- bumpMultiplier: BUMP_MULTIPLIER,
220
- easeExponent: EASE_EXPONENT,
221
- onUpdate: () => {
222
- const renderWindow = genericRenderWindow.value!.getRenderWindow();
223
- renderWindow.render();
224
- },
225
- onEnd: () => {
226
- is_moving.value = false;
227
- performSyncRemoteCamera();
228
- },
229
- });
113
+ const position = camera.getPosition();
114
+ const directionOfProjection = camera.getDirectionOfProjection();
115
+ const distance = dot(
116
+ [
117
+ pickedPosition[0] - position[0],
118
+ pickedPosition[1] - position[1],
119
+ pickedPosition[2] - position[2],
120
+ ],
121
+ directionOfProjection,
122
+ );
123
+ camera.setFocalPoint(...pickedPosition);
124
+ camera.setPosition(
125
+ pickedPosition[0] - distance * directionOfProjection[0],
126
+ pickedPosition[1] - distance * directionOfProjection[1],
127
+ pickedPosition[2] - distance * directionOfProjection[2],
128
+ );
230
129
  }
231
130
 
232
- function useHybridViewerCamera() {
131
+ // Shared via createSharedComposable (rather than merged into the parent hybridViewer store) so sibling slices, e.g. viewport.ts and ruler.ts, can read camera_options/setCamera/syncRemoteCamera directly without importing the parent store and creating a cycle. A Pinia store would work too but its $id/$patch/... properties would leak into the composed store's spread and collapse its inferred type.
132
+ const useHybridViewerCamera = createSharedComposable(() => {
233
133
  const camera_options = reactive<Record<string, unknown>>({});
134
+
135
+ function performSyncRemoteCamera(): void {
136
+ const { genericRenderWindow, remoteRender } = useHybridViewerCore();
137
+ const viewerStore = useViewerStore();
138
+ const renderer = requireRenderWindow(genericRenderWindow).getRenderer();
139
+ const camera = renderer.getActiveCamera();
140
+ const options_camera = getCameraOptions(camera);
141
+ const schema = viewer_schemas.opengeodeweb_viewer.viewer.update_camera;
142
+ const params = {
143
+ camera_options: options_camera,
144
+ };
145
+ viewerStore
146
+ .request(
147
+ {
148
+ schema,
149
+ params,
150
+ },
151
+ {
152
+ response_function: async () => {
153
+ await remoteRender().catch(() => undefined);
154
+ Object.assign(camera_options, options_camera);
155
+ },
156
+ },
157
+ )
158
+ // oxlint-disable-next-line promise/prefer-await-to-then -- fire-and-forget inside a sync caller; see codebase convention in global_attribute_style.ts.
159
+ .catch(() => undefined);
160
+ }
161
+
234
162
  function syncRemoteCamera(): void {
235
163
  performSyncRemoteCamera();
236
164
  }
165
+
166
+ function performSetCamera(targetCameraOptions: CameraOptions): void {
167
+ const { genericRenderWindow, is_moving } = useHybridViewerCore();
168
+ const imageStyle = getImageStyle();
169
+ const renderer = requireRenderWindow(genericRenderWindow).getRenderer();
170
+ const camera = renderer.getActiveCamera();
171
+ const startState = getCameraOptions(camera);
172
+ const duration = computeAnimationDuration(startState, targetCameraOptions);
173
+ is_moving.value = true;
174
+ if (imageStyle) {
175
+ imageStyle.opacity = "0";
176
+ }
177
+ animateCamera({
178
+ camera,
179
+ startState,
180
+ targetState: targetCameraOptions,
181
+ duration,
182
+ bumpMultiplier: 0,
183
+ easeExponent: EASE_EXPONENT,
184
+ onUpdate: () => {
185
+ const renderWindow = requireRenderWindow(genericRenderWindow).getRenderWindow();
186
+ renderWindow.render();
187
+ },
188
+ onEnd: () => {
189
+ applyCameraOptions(camera, targetCameraOptions);
190
+ const renderWindow = requireRenderWindow(genericRenderWindow).getRenderWindow();
191
+ renderWindow.render();
192
+ is_moving.value = false;
193
+ performSyncRemoteCamera();
194
+ },
195
+ });
196
+ }
197
+
237
198
  function setCamera(targetCameraOptions: CameraOptions): void {
238
199
  performSetCamera(targetCameraOptions);
239
200
  }
201
+
240
202
  function resetCamera(): void {
241
- const { genericRenderWindow } = useHybridViewerStore() as unknown as HybridViewerStorePublic;
242
- const renderer = genericRenderWindow.value!.getRenderer();
203
+ const { genericRenderWindow } = useHybridViewerCore();
204
+ const renderer = requireRenderWindow(genericRenderWindow).getRenderer();
243
205
  renderer.resetCamera();
244
- const renderWindow = genericRenderWindow.value!.getRenderWindow();
206
+ const renderWindow = requireRenderWindow(genericRenderWindow).getRenderWindow();
245
207
  renderWindow.render();
246
208
  syncRemoteCamera();
247
209
  }
210
+
211
+ async function performFocusCameraOnObject(id: string, block_ids: string[] = []): Promise<void> {
212
+ const { genericRenderWindow } = useHybridViewerCore();
213
+ const { hybridDb } = useHybridViewerScene();
214
+ if (!hybridDb[id]) {
215
+ return;
216
+ }
217
+ const viewerStore = useViewerStore();
218
+ const bounds: [number, number, number, number, number, number] =
219
+ block_ids.length > 0
220
+ ? toBounds(
221
+ await viewerStore.request({
222
+ schema: viewer_schemas.opengeodeweb_viewer.model.get_blocks_bounds,
223
+ params: {
224
+ id,
225
+ block_ids,
226
+ },
227
+ }),
228
+ )
229
+ : hybridDb[id].actor.getBounds();
230
+ const renderer = requireRenderWindow(genericRenderWindow).getRenderer();
231
+ const camera = renderer.getActiveCamera();
232
+ const startOptions = getCameraOptions(camera);
233
+ renderer.resetCamera(bounds);
234
+ const targetOptions = getCameraOptions(camera);
235
+ applyCameraOptions(camera, startOptions);
236
+ performSetCamera(targetOptions);
237
+ }
238
+
248
239
  async function focusCameraOnObject(id: string, block_ids: string[] = []): Promise<void> {
249
240
  await performFocusCameraOnObject(id, block_ids);
250
241
  }
242
+
243
+ function performCameraOrientation(orientation: string): void {
244
+ const config = ORIENTATIONS[orientation.toLowerCase()];
245
+ if (!config) {
246
+ return;
247
+ }
248
+ const { genericRenderWindow, is_moving } = useHybridViewerCore();
249
+ const imageStyle = getImageStyle();
250
+ const renderer = requireRenderWindow(genericRenderWindow).getRenderer();
251
+ const camera = renderer.getActiveCamera();
252
+ const startState = getCameraOptions(camera);
253
+ applyCameraOptions(camera, {
254
+ ...config,
255
+ focal_point: [0, 0, 0],
256
+ });
257
+ renderer.resetCamera();
258
+ const targetState = getCameraOptions(camera);
259
+ applyCameraOptions(camera, startState);
260
+ const alignment = dot(camera.getDirectionOfProjection(), config.position);
261
+ const duration =
262
+ alignment > ALIGNMENT_THRESHOLD ? LONG_ANIMATION_DURATION : SHORT_ANIMATION_DURATION;
263
+ is_moving.value = true;
264
+ if (imageStyle) {
265
+ imageStyle.opacity = "0";
266
+ }
267
+ animateCamera({
268
+ camera,
269
+ startState,
270
+ targetState,
271
+ duration,
272
+ bumpMultiplier: BUMP_MULTIPLIER,
273
+ easeExponent: EASE_EXPONENT,
274
+ onUpdate: () => {
275
+ const renderWindow = requireRenderWindow(genericRenderWindow).getRenderWindow();
276
+ renderWindow.render();
277
+ },
278
+ onEnd: () => {
279
+ is_moving.value = false;
280
+ performSyncRemoteCamera();
281
+ },
282
+ });
283
+ }
284
+
251
285
  function setCameraOrientation(orientation: string): void {
252
286
  performCameraOrientation(orientation);
253
287
  }
288
+
254
289
  return {
255
290
  camera_options,
256
291
  syncRemoteCamera,
@@ -259,24 +294,7 @@ function useHybridViewerCamera() {
259
294
  focusCameraOnObject,
260
295
  setCameraOrientation,
261
296
  };
262
- }
263
-
264
- function centerCameraOnPosition(
265
- camera: vtkCamera | undefined,
266
- pickedPosition: Vector3 | undefined,
267
- ): void {
268
- if (!camera || !pickedPosition) {
269
- return;
270
- }
271
- const focalPoint = camera.getFocalPoint();
272
- const position = camera.getPosition();
273
- camera.setFocalPoint(...pickedPosition);
274
- camera.setPosition(
275
- position[0] + pickedPosition[0] - focalPoint[0],
276
- position[1] + pickedPosition[1] - focalPoint[1],
277
- position[2] + pickedPosition[2] - focalPoint[2],
278
- );
279
- }
297
+ });
280
298
 
281
299
  interface CameraSnapshot {
282
300
  zScale?: number;
@@ -287,12 +305,11 @@ async function applySnapshot(snapshot: CameraSnapshot | undefined): Promise<void
287
305
  if (!snapshot) {
288
306
  return;
289
307
  }
290
- const { setZScaling, setCamera } = useHybridViewerStore() as unknown as HybridViewerStorePublic;
291
308
  if (typeof snapshot.zScale === "number") {
292
- await setZScaling(snapshot.zScale);
309
+ await useHybridViewerScene().setZScaling(snapshot.zScale);
293
310
  }
294
311
  if (snapshot.camera_options) {
295
- setCamera(snapshot.camera_options);
312
+ useHybridViewerCamera().setCamera(snapshot.camera_options);
296
313
  }
297
314
  }
298
315
  export {
@@ -304,9 +321,6 @@ export {
304
321
  applySnapshot,
305
322
  centerCameraOnPosition,
306
323
  getCameraOptions,
307
- performCameraOrientation,
308
- performFocusCameraOnObject,
309
- performSetCamera,
310
- performSyncRemoteCamera,
311
324
  useHybridViewerCamera,
312
325
  };
326
+ export type { CameraSnapshot };
@@ -1,7 +1,5 @@
1
- // Not auto-fixable (eslint's sort-imports core rule has no autofixer) and this file's import order doesn't match its syntax-kind-then-alphabetical requirement - left as-is rather than manually reordered across the codebase for a purely cosmetic rule.
2
- // oxlint-disable eslint/sort-imports
3
- import { dot } from "@kitware/vtk.js/Common/Core/Math";
4
1
  import type { CameraOptions, Vector3, vtkCamera } from "./vtk_types";
2
+ import { dot } from "@kitware/vtk.js/Common/Core/Math";
5
3
 
6
4
  const NEAR_ZERO_THRESHOLD = 1e-10;
7
5
  const SLERP_LINEAR_THRESHOLD = 0.9995;
@@ -117,15 +115,18 @@ function animateCamera(options: AnimateCameraOptions): void {
117
115
  const bump = bumpMultiplier * Math.sin(Math.PI * progress);
118
116
  const dir = slerp(startDir, targetDir, ease, antipodalMid);
119
117
  const dist = startDist + (targetDist - startDist) * ease + bump;
120
- // `index` ranges over startState.focal_point's own length (3), which always
121
- // Matches targetState.focal_point's length, so the lookup is always in bounds.
122
- const focalPoint = startState.focal_point.map(
123
- (startValue, index) => startValue + (targetState.focal_point[index]! - startValue) * ease,
124
- );
118
+ const focalPoint: Vector3 = [
119
+ startState.focal_point[0] + (targetState.focal_point[0] - startState.focal_point[0]) * ease,
120
+ startState.focal_point[1] + (targetState.focal_point[1] - startState.focal_point[1]) * ease,
121
+ startState.focal_point[2] + (targetState.focal_point[2] - startState.focal_point[2]) * ease,
122
+ ];
125
123
  const viewUp = slerp(startState.view_up, targetState.view_up, ease);
126
124
  camera.set({
127
- // Same reasoning: `index` ranges over focalPoint's length, matching `dir`'s length (3).
128
- position: focalPoint.map((focalCoord, index) => focalCoord + dir[index]! * dist),
125
+ position: [
126
+ focalPoint[0] + dir[0] * dist,
127
+ focalPoint[1] + dir[1] * dist,
128
+ focalPoint[2] + dir[2] * dist,
129
+ ],
129
130
  viewUp,
130
131
  focalPoint,
131
132
  });
@@ -0,0 +1,64 @@
1
+ import { Status } from "@ogw_front/utils/status";
2
+ import type { ViewStreamLike } from "./vtk_types";
3
+ import type { vtkGenericRenderWindow as VtkGenericRenderWindow } from "@kitware/vtk.js/Rendering/Misc/GenericRenderWindow";
4
+ import { useViewerStore } from "@ogw_front/stores/viewer";
5
+ import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json";
6
+
7
+ interface GenericRenderWindowHolder {
8
+ value?: VtkGenericRenderWindow;
9
+ }
10
+
11
+ // `genericRenderWindow.value` is only ever read from these functions after the viewer has been initialized (see app/stores/hybrid_viewer.ts's initHybridViewer), so requireRenderWindow throwing on that path would indicate a real bug rather than an expected state.
12
+ function requireRenderWindow(holder: GenericRenderWindowHolder): VtkGenericRenderWindow {
13
+ if (!holder.value) {
14
+ throw new Error("genericRenderWindow accessed before the viewer was initialized");
15
+ }
16
+ return holder.value;
17
+ }
18
+
19
+ // Render-window, status and drag/pick state shared across the hybrid viewer's slice composables (app/stores/hybrid_viewer.ts and internal/stores/hybrid_viewer/*), plus the debounced remote-render call they all trigger. Shared via createSharedComposable (a singleton per mounted tree, not a Pinia store) so those slices can depend on it directly instead of on the composed hybridViewer store, which would create an import cycle; a real Pinia store here would also pull its own $id/$patch/... properties into the composed store's spread and collapse its inferred type.
20
+ const useHybridViewerCore = createSharedComposable(() => {
21
+ const genericRenderWindow = reactive<GenericRenderWindowHolder>({});
22
+ const status = ref(Status.NOT_CREATED);
23
+ const is_moving = ref(false);
24
+ const is_picking = ref(false);
25
+ const viewStream = ref<ViewStreamLike | undefined>(undefined);
26
+
27
+ let renderPromise: Promise<void> | undefined = undefined;
28
+ let renderPending = false;
29
+
30
+ async function remoteRender(): Promise<void> {
31
+ if (renderPromise) {
32
+ renderPending = true;
33
+ await renderPromise;
34
+ return;
35
+ }
36
+
37
+ renderPromise = (async (): Promise<void> => {
38
+ try {
39
+ const viewerStore = useViewerStore();
40
+ const schema = viewer_schemas.opengeodeweb_viewer.viewer.render;
41
+ await viewerStore.request({ schema });
42
+ } finally {
43
+ renderPromise = undefined;
44
+ if (renderPending) {
45
+ renderPending = false;
46
+ await remoteRender();
47
+ }
48
+ }
49
+ })();
50
+ return renderPromise;
51
+ }
52
+
53
+ return {
54
+ genericRenderWindow,
55
+ status,
56
+ is_moving,
57
+ is_picking,
58
+ viewStream,
59
+ remoteRender,
60
+ };
61
+ });
62
+
63
+ export { requireRenderWindow, useHybridViewerCore };
64
+ export type { GenericRenderWindowHolder };
@@ -1,13 +1,10 @@
1
- // Not auto-fixable (eslint's sort-imports core rule has no autofixer) and this file's import order doesn't match its syntax-kind-then-alphabetical requirement - left as-is rather than manually reordered across the codebase for a purely cosmetic rule.
2
- // oxlint-disable eslint/sort-imports
3
- import { useHybridViewerStore } from "@ogw_front/stores/hybrid_viewer";
1
+ import { useHybridViewerCore } from "./core";
4
2
  import { useViewerStore } from "@ogw_front/stores/viewer";
5
3
  import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json";
6
- import type { HybridViewerStorePublic } from "./vtk_types";
7
4
 
8
- async function performSetClippingPlanes(ids: string[], planes: unknown): Promise<void> {
5
+ async function setClippingPlanes(ids: string[], planes: unknown): Promise<void> {
9
6
  const viewerStore = useViewerStore();
10
- const { remoteRender } = useHybridViewerStore() as unknown as HybridViewerStorePublic;
7
+ const { remoteRender } = useHybridViewerCore();
11
8
  const schema = viewer_schemas.opengeodeweb_viewer.viewer.clipping_planes;
12
9
  const params = {
13
10
  ids,
@@ -19,9 +16,9 @@ async function performSetClippingPlanes(ids: string[], planes: unknown): Promise
19
16
  });
20
17
  await remoteRender();
21
18
  }
22
- async function performSetShrink(ids: string[], shrink_factor: number): Promise<void> {
19
+ async function setShrink(ids: string[], shrink_factor: number): Promise<void> {
23
20
  const viewerStore = useViewerStore();
24
- const { remoteRender } = useHybridViewerStore() as unknown as HybridViewerStorePublic;
21
+ const { remoteRender } = useHybridViewerCore();
25
22
  const schema = viewer_schemas.opengeodeweb_viewer.viewer.shrink;
26
23
  const params = {
27
24
  ids,
@@ -33,16 +30,13 @@ async function performSetShrink(ids: string[], shrink_factor: number): Promise<v
33
30
  });
34
31
  await remoteRender();
35
32
  }
36
- function useHybridViewerFilters() {
37
- async function setClippingPlanes(ids: string[], planes: unknown): Promise<void> {
38
- await performSetClippingPlanes(ids, planes);
39
- }
40
- async function setShrink(ids: string[], shrink_factor: number): Promise<void> {
41
- await performSetShrink(ids, shrink_factor);
42
- }
33
+ function useHybridViewerFilters(): {
34
+ setClippingPlanes: (ids: string[], planes: unknown) => Promise<void>;
35
+ setShrink: (ids: string[], shrink_factor: number) => Promise<void>;
36
+ } {
43
37
  return {
44
38
  setClippingPlanes,
45
39
  setShrink,
46
40
  };
47
41
  }
48
- export { performSetClippingPlanes, performSetShrink, useHybridViewerFilters };
42
+ export { useHybridViewerFilters };