@geode/opengeodeweb-front 10.31.0 → 10.32.0-rc.1

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.
@@ -1,6 +1,6 @@
1
1
  <script setup>
2
2
  import ToolPanel from "@ogw_front/components/ToolPanel";
3
- import { applyCameraOptions } from "@ogw_internal/stores/hybrid_viewer_camera";
3
+ import { applyCameraOptions } from "@ogw_internal/stores/hybrid_viewer/camera";
4
4
  import { useHybridViewerStore } from "@ogw_front/stores/hybrid_viewer";
5
5
  import { newInstance as vtkAnnotatedCubeActor } from "@kitware/vtk.js/Rendering/Core/AnnotatedCubeActor";
6
6
  import { newInstance as vtkGenericRenderWindow } from "@kitware/vtk.js/Rendering/Misc/GenericRenderWindow";
@@ -55,6 +55,14 @@ function debounce(func, wait) {
55
55
 
56
56
  async function handleClick(event) {
57
57
  const { offsetX, offsetY, clientX, clientY } = event;
58
+ if (hybridViewerStore.is_ruler_active) {
59
+ const rect = container.value.$el.getBoundingClientRect();
60
+ const x = event.clientX - rect.left;
61
+ const y = elementHeight.value - (event.clientY - rect.top);
62
+ await hybridViewerStore.handleRulerClick(x, y);
63
+ return;
64
+ }
65
+
58
66
  if (viewerStore.picking_mode) {
59
67
  const rect = container.value.$el.getBoundingClientRect();
60
68
  const x = event.clientX - rect.left;
@@ -0,0 +1,144 @@
1
+ <script setup>
2
+ import ToolPanel from "@ogw_front/components/ToolPanel";
3
+ import { useHybridViewerStore } from "@ogw_front/stores/hybrid_viewer";
4
+
5
+ const show = defineModel("show", { type: Boolean, default: false });
6
+ const hybridViewerStore = useHybridViewerStore();
7
+
8
+ const localPoint1 = ref([0, 0, 0]);
9
+ const localPoint2 = ref([0, 0, 0]);
10
+
11
+ watch(
12
+ () => hybridViewerStore.ruler_point1,
13
+ (point) => {
14
+ if (point) {
15
+ localPoint1.value = [...point];
16
+ }
17
+ },
18
+ );
19
+
20
+ watch(
21
+ () => hybridViewerStore.ruler_point2,
22
+ (point) => {
23
+ if (point) {
24
+ localPoint2.value = [...point];
25
+ }
26
+ },
27
+ );
28
+
29
+ watch(show, (visible) => {
30
+ if (visible) {
31
+ hybridViewerStore.is_ruler_active = true;
32
+ } else if (hybridViewerStore.is_ruler_active) {
33
+ hybridViewerStore.deactivateRuler();
34
+ }
35
+ });
36
+
37
+ watch(
38
+ () => hybridViewerStore.is_ruler_active,
39
+ (active) => {
40
+ if (!active) {
41
+ show.value = false;
42
+ }
43
+ },
44
+ );
45
+
46
+ async function applyManualCoords() {
47
+ hybridViewerStore.ruler_point1 = [...localPoint1.value];
48
+ hybridViewerStore.ruler_point2 = [...localPoint2.value];
49
+ await hybridViewerStore.applyRuler();
50
+ }
51
+ </script>
52
+
53
+ <template>
54
+ <ToolPanel
55
+ v-model="show"
56
+ title="Ruler"
57
+ :width="300"
58
+ close-label="Stop ruler"
59
+ :click-outside="false"
60
+ >
61
+ <v-card-text class="pa-3">
62
+ <v-switch
63
+ v-model="hybridViewerStore.ruler_snap"
64
+ data-testid="rulerSnapToggle"
65
+ label="Snap to vertex"
66
+ color="primary"
67
+ density="compact"
68
+ hide-details
69
+ class="mb-4"
70
+ />
71
+
72
+ <v-divider class="mb-3" />
73
+
74
+ <div class="text-caption font-weight-bold mb-2 text-medium-emphasis">Point 1</div>
75
+ <v-row dense class="mb-1" data-testid="rulerPointCard">
76
+ <v-col v-for="(axis, index) in ['X', 'Y', 'Z']" :key="axis">
77
+ <v-text-field
78
+ v-model.number="localPoint1[index]"
79
+ data-testid="rulerPointCoordInput"
80
+ :label="axis"
81
+ type="number"
82
+ density="compact"
83
+ variant="outlined"
84
+ hide-details
85
+ />
86
+ </v-col>
87
+ </v-row>
88
+
89
+ <div class="text-caption font-weight-bold mb-2 mt-3 text-medium-emphasis">Point 2</div>
90
+ <v-row dense class="mb-1" data-testid="rulerPointCard">
91
+ <v-col v-for="(axis, index) in ['X', 'Y', 'Z']" :key="axis">
92
+ <v-text-field
93
+ v-model.number="localPoint2[index]"
94
+ data-testid="rulerPointCoordInput"
95
+ :label="axis"
96
+ type="number"
97
+ density="compact"
98
+ variant="outlined"
99
+ hide-details
100
+ />
101
+ </v-col>
102
+ </v-row>
103
+
104
+ <v-btn
105
+ data-testid="rulerApplyButton"
106
+ variant="tonal"
107
+ size="small"
108
+ color="primary"
109
+ block
110
+ class="mt-3 text-caption text-none"
111
+ @click="applyManualCoords"
112
+ >
113
+ Apply
114
+ </v-btn>
115
+
116
+ <v-divider class="my-3" />
117
+
118
+ <div v-if="hybridViewerStore.ruler_distance !== undefined" class="text-center">
119
+ <div class="text-caption text-medium-emphasis mb-1">Distance</div>
120
+ <div data-testid="rulerDistance" class="text-h6 font-weight-bold">
121
+ {{ hybridViewerStore.ruler_distance.toFixed(4) }}
122
+ </div>
123
+ </div>
124
+ <div v-else class="text-caption text-medium-emphasis text-center">
125
+ Click in the scene to set both points
126
+ </div>
127
+ </v-card-text>
128
+
129
+ <template #actions>
130
+ <v-card-actions class="justify-center pb-3 pt-0">
131
+ <v-btn
132
+ data-testid="rulerClearButton"
133
+ variant="text"
134
+ size="small"
135
+ color="error"
136
+ class="text-caption text-none"
137
+ @click="hybridViewerStore.clearRuler()"
138
+ >
139
+ Clear
140
+ </v-btn>
141
+ </v-card-actions>
142
+ </template>
143
+ </ToolPanel>
144
+ </template>
@@ -4,6 +4,7 @@ import CameraBookmarkIcon from "@ogw_front/assets/viewer_svgs/camera-bookmark.sv
4
4
  import CameraManager from "@ogw_front/components/CameraManager";
5
5
  import CameraOrientation from "@ogw_front/components/CameraOrientation";
6
6
  import ClippingPlanes from "@ogw_front/components/ClippingPlanes";
7
+ import Ruler from "@ogw_front/components/Ruler";
7
8
  import Screenshot from "@ogw_front/components/Screenshot";
8
9
  import ShrinkFilter from "@ogw_front/components/ShrinkFilter";
9
10
  import ZScaling from "@ogw_front/components/ZScaling";
@@ -19,6 +20,7 @@ const showCameraOrientation = ref(false);
19
20
  const showZScaling = ref(false);
20
21
  const showClippingPlanes = ref(false);
21
22
  const showShrinkFilter = ref(false);
23
+ const showRuler = ref(false);
22
24
  const grid_scale = ref(false);
23
25
  const zScale = ref(hybridViewerStore.zScale);
24
26
 
@@ -173,6 +175,15 @@ const camera_options = computed(() => [
173
175
  showShrinkFilter.value = !showShrinkFilter.value;
174
176
  },
175
177
  },
178
+ {
179
+ testId: "rulerButton",
180
+ tooltip: "Ruler",
181
+ icon: "mdi-ruler",
182
+ color: showRuler.value ? "primary" : undefined,
183
+ action: () => {
184
+ showRuler.value = !showRuler.value;
185
+ },
186
+ },
176
187
  ]);
177
188
  </script>
178
189
 
@@ -247,6 +258,7 @@ const camera_options = computed(() => [
247
258
  />
248
259
  <ClippingPlanes v-model:show="showClippingPlanes" />
249
260
  <ShrinkFilter v-model:show="showShrinkFilter" />
261
+ <Ruler v-model:show="showRuler" />
250
262
  </template>
251
263
 
252
264
  <style module>
@@ -25,7 +25,9 @@ function stopHoverHighlight() {
25
25
  }
26
26
 
27
27
  onKeyStroke("Escape", () => {
28
- if (hybridViewerStore.is_hover_highlight) {
28
+ if (hybridViewerStore.is_ruler_active) {
29
+ hybridViewerStore.deactivateRuler();
30
+ } else if (hybridViewerStore.is_hover_highlight) {
29
31
  stopHoverHighlight();
30
32
  }
31
33
  });
@@ -129,6 +131,30 @@ defineExpose({ get_viewer_id });
129
131
  </v-chip>
130
132
  </div>
131
133
  </v-fade-transition>
134
+
135
+ <v-fade-transition>
136
+ <div
137
+ v-if="hybridViewerStore.is_ruler_active"
138
+ class="picking-message-container d-flex justify-center w-100 pa-4"
139
+ >
140
+ <v-chip
141
+ data-testid="rulerActiveChip"
142
+ color="secondary"
143
+ elevation="8"
144
+ size="large"
145
+ variant="flat"
146
+ class="pick-pulse"
147
+ style="pointer-events: auto"
148
+ prepend-icon="mdi-ruler"
149
+ @click="hybridViewerStore.clearRuler()"
150
+ >
151
+ Ruler &mdash; click to set point {{ hybridViewerStore.ruler_awaiting_point }}
152
+ &middot; Esc to stop
153
+ <v-divider vertical class="mx-2 my-1" opacity="0.3" />
154
+ <v-icon icon="mdi-close" size="small" />
155
+ </v-chip>
156
+ </div>
157
+ </v-fade-transition>
132
158
  </template>
133
159
 
134
160
  <style scoped>
@@ -1,81 +1,52 @@
1
- import {
2
- ACTOR_COLOR,
3
- BACKGROUND_COLOR,
4
- WHEEL_TIME_OUT_MS,
5
- } from "@ogw_internal/stores/hybrid_viewer_constants";
6
1
  import {
7
2
  applySnapshot,
8
3
  getCameraOptions,
9
- performCameraOrientation,
10
- performFocusCameraOnObject,
11
- performSetCamera,
12
- performSyncRemoteCamera,
13
- } from "@ogw_internal/stores/hybrid_viewer_camera";
14
- import {
15
- createClearHoverData,
16
- createHoverHighlight,
17
- } from "@ogw_internal/stores/hybrid_viewer_highlight";
18
- import {
19
- performAddItem,
20
- performClear,
21
- performClearHoverHighlight,
22
- performClickPicking,
23
- performRemoveItem,
24
- performResize,
25
- performSetClippingPlanes,
26
- performSetContainer,
27
- performSetShrink,
28
- performSetVisibility,
29
- performSetZScaling,
30
- } from "@ogw_internal/stores/hybrid_viewer";
31
- import { computeAverageBrightness } from "@ogw_internal/stores/hybrid_viewer_brightness";
32
- import { newInstance as vtkActor } from "@kitware/vtk.js/Rendering/Core/Actor";
4
+ useHybridViewerCamera,
5
+ } from "@ogw_internal/stores/hybrid_viewer/camera";
6
+ import { BACKGROUND_COLOR } from "@ogw_internal/stores/hybrid_viewer/constants";
7
+ import { useHybridViewerBrightness } from "@ogw_internal/stores/hybrid_viewer/brightness";
8
+ import { useHybridViewerFilters } from "@ogw_internal/stores/hybrid_viewer/filters";
9
+ import { useHybridViewerHighlight } from "@ogw_internal/stores/hybrid_viewer/highlight";
10
+ import { useHybridViewerRuler } from "@ogw_internal/stores/hybrid_viewer/ruler";
11
+ import { useHybridViewerScene } from "@ogw_internal/stores/hybrid_viewer/scene";
12
+ import { useHybridViewerViewport } from "@ogw_internal/stores/hybrid_viewer/viewport";
33
13
  import { newInstance as vtkGenericRenderWindow } from "@kitware/vtk.js/Rendering/Misc/GenericRenderWindow";
34
- import { newInstance as vtkMapper } from "@kitware/vtk.js/Rendering/Core/Mapper";
35
- import { newInstance as vtkXMLPolyDataReader } from "@kitware/vtk.js/IO/XML/XMLPolyDataReader";
36
14
 
37
15
  import { Status } from "@ogw_front/utils/status";
38
- import { useDataStore } from "@ogw_front/stores/data";
39
16
  import { useViewerStore } from "@ogw_front/stores/viewer";
40
17
 
41
18
  import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json";
42
19
 
43
20
  // oxlint-disable max-lines-per-function, max-statements
44
21
  export const useHybridViewerStore = defineStore("hybridViewer", () => {
45
- const dataStore = useDataStore();
46
- const hybridDb = reactive({});
47
22
  const viewerStore = useViewerStore();
48
- const camera_options = reactive({});
49
23
  const genericRenderWindow = reactive({});
50
24
  const status = ref(Status.NOT_CREATED);
51
25
  const is_moving = ref(false);
52
26
  const is_picking = ref(false);
53
- const is_hover_highlight = ref(false);
54
- const hover_highlight_field_type = ref("CELL");
55
- const hoverData = ref(undefined);
56
- const hoverPosition = ref({ x: 0, y: 0 });
57
- const zScale = ref(1);
58
27
  let imageStyle = undefined;
59
- let viewStream = undefined;
60
- let wheelEventEndTimeout = undefined;
61
- const gridActor = undefined;
62
28
 
63
- watch(is_picking, (value) => {
64
- const element = genericRenderWindow.value
65
- ?.getApiSpecificRenderWindow()
66
- ?.getCanvas()?.parentElement;
67
- if (element) {
68
- element.style.cursor = value ? "crosshair" : "default";
29
+ const brightnessStore = useHybridViewerBrightness();
30
+ const sceneStore = useHybridViewerScene();
31
+ const filtersStore = useHybridViewerFilters();
32
+ const highlightStore = useHybridViewerHighlight();
33
+ const rulerStore = useHybridViewerRuler();
34
+ const cameraStore = useHybridViewerCamera();
35
+ const viewportStore = useHybridViewerViewport();
36
+
37
+ const is_cursor_crosshair = computed(() => is_picking.value || rulerStore.is_ruler_active.value);
38
+
39
+ watch(is_cursor_crosshair, (value) => {
40
+ if (!genericRenderWindow.value) {
41
+ return;
42
+ }
43
+ const webGLRenderWindow = genericRenderWindow.value.getApiSpecificRenderWindow();
44
+ const canvas = webGLRenderWindow.getCanvas();
45
+ if (canvas && canvas.parentElement) {
46
+ canvas.parentElement.style.cursor = value ? "crosshair" : "default";
69
47
  }
70
48
  });
71
49
 
72
- const latestImage = ref(undefined);
73
- const offscreenCanvas =
74
- typeof document === "undefined" ? undefined : document.createElement("canvas");
75
- const offscreenCtx = offscreenCanvas
76
- ? offscreenCanvas.getContext("2d", { willReadFrequently: true })
77
- : undefined;
78
-
79
50
  async function initHybridViewer() {
80
51
  if (status.value !== Status.NOT_CREATED) {
81
52
  return;
@@ -89,112 +60,25 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => {
89
60
  imageStyle = webGLRenderWindow.getReferenceByName("bgImage").style;
90
61
  Object.assign(imageStyle, { transition: "opacity 0.1s ease-in", zIndex: 1 });
91
62
  await viewerStore.ws_connect();
92
- viewStream = viewerStore.client.getImageStream().createViewStream("-1");
93
- viewStream.onImageReady((event) => {
63
+ const imageStream = viewerStore.client.getImageStream();
64
+ viewportStore.viewStream.value = imageStream.createViewStream("-1");
65
+ viewportStore.viewStream.value.onImageReady((event) => {
94
66
  if (is_moving.value) {
95
67
  return;
96
68
  }
97
- latestImage.value = event.image;
69
+ brightnessStore.latestImage.value = event.image;
98
70
  webGLRenderWindow.setBackgroundImage(event.image);
99
71
  imageStyle.opacity = 1;
100
72
  });
101
- const camera = genericRenderWindow.value.getRenderer().getActiveCamera();
102
- Object.assign(camera_options, getCameraOptions(camera));
73
+ const renderer = genericRenderWindow.value.getRenderer();
74
+ const camera = renderer.getActiveCamera();
75
+ Object.assign(cameraStore.camera_options, getCameraOptions(camera));
103
76
  camera.onModified(() => {
104
- Object.assign(camera_options, getCameraOptions(camera));
77
+ Object.assign(cameraStore.camera_options, getCameraOptions(camera));
105
78
  });
106
79
  status.value = Status.CREATED;
107
80
  }
108
81
 
109
- async function addItem(id) {
110
- await performAddItem(id, {
111
- genericRenderWindow: genericRenderWindow.value,
112
- dataStore,
113
- vtkXMLPolyDataReader,
114
- vtkActor,
115
- vtkMapper,
116
- actorColor: ACTOR_COLOR,
117
- hybridDb,
118
- });
119
- }
120
-
121
- function removeItem(id) {
122
- performRemoveItem(id, { genericRenderWindow: genericRenderWindow.value, hybridDb });
123
- }
124
-
125
- function setVisibility(id, visibility) {
126
- performSetVisibility(id, visibility, {
127
- genericRenderWindow: genericRenderWindow.value,
128
- hybridDb,
129
- });
130
- }
131
-
132
- async function setZScaling(z_scale) {
133
- await performSetZScaling(z_scale, {
134
- zScale,
135
- genericRenderWindow: genericRenderWindow.value,
136
- gridActor,
137
- viewerStore,
138
- viewer_schemas,
139
- remoteRender,
140
- });
141
- }
142
-
143
- async function setClippingPlanes(ids, planes) {
144
- await performSetClippingPlanes(ids, planes, { viewerStore, viewer_schemas, remoteRender });
145
- }
146
-
147
- async function setShrink(ids, shrink_factor) {
148
- await performSetShrink(ids, shrink_factor, { viewerStore, viewer_schemas, remoteRender });
149
- }
150
-
151
- function resetCamera() {
152
- genericRenderWindow.value.getRenderer().resetCamera();
153
- genericRenderWindow.value.getRenderWindow().render();
154
- syncRemoteCamera();
155
- }
156
-
157
- async function focusCameraOnObject(id, block_ids = []) {
158
- await performFocusCameraOnObject(id, {
159
- hybridDb,
160
- viewerStore,
161
- viewer_schemas,
162
- genericRenderWindow: genericRenderWindow.value,
163
- block_ids,
164
- is_moving,
165
- imageStyle,
166
- syncRemoteCamera,
167
- });
168
- }
169
-
170
- function setCameraOrientation(orientation) {
171
- performCameraOrientation(orientation, {
172
- genericRenderWindow: genericRenderWindow.value,
173
- is_moving,
174
- imageStyle,
175
- syncRemoteCamera,
176
- });
177
- }
178
-
179
- function setCamera(targetCameraOptions) {
180
- performSetCamera(targetCameraOptions, {
181
- genericRenderWindow: genericRenderWindow.value,
182
- is_moving,
183
- imageStyle,
184
- syncRemoteCamera,
185
- });
186
- }
187
-
188
- function syncRemoteCamera() {
189
- performSyncRemoteCamera({
190
- genericRenderWindow: genericRenderWindow.value,
191
- viewerStore,
192
- viewer_schemas,
193
- remoteRender,
194
- camera_options,
195
- });
196
- }
197
-
198
82
  let renderPromise = undefined;
199
83
  let renderPending = false;
200
84
 
@@ -219,124 +103,30 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => {
219
103
  return renderPromise;
220
104
  }
221
105
 
222
- const hoverTimeoutRef = ref(undefined);
223
- const currentHoverId = ref(undefined);
224
-
225
- const clearHoverData = createClearHoverData(hoverTimeoutRef, hoverData, currentHoverId);
226
-
227
- const hoverHighlight = createHoverHighlight({
228
- genericRenderWindow,
229
- is_hover_highlight,
230
- viewerStore,
231
- viewer_schemas,
232
- hover_highlight_field_type,
233
- hybridDb,
234
- hoverData,
235
- hoverPosition,
236
- currentHoverId,
237
- hoverTimeoutRef,
238
- clearHoverData,
239
- });
240
-
241
- function clearHoverHighlight() {
242
- clearHoverData();
243
- performClearHoverHighlight({
244
- viewerStore,
245
- viewer_schemas,
246
- hover_highlight_field_type,
247
- hybridDb,
248
- });
249
- }
250
-
251
- function setContainer(container) {
252
- performSetContainer({
253
- container,
254
- genericRenderWindow: genericRenderWindow.value,
255
- imageStyleSetter: (style) => (imageStyle = style),
256
- resize,
257
- useMousePressed,
258
- useEventListener,
259
- is_picking,
260
- is_moving,
261
- clickPickingCallback: performClickPicking,
262
- viewerStore,
263
- viewer_schemas,
264
- syncRemoteCamera,
265
- hoverHighlight,
266
- wheelTimeoutMs: WHEEL_TIME_OUT_MS,
267
- wheelEventEndTimeout,
268
- wheelTimeoutSetter: (timeout) => (wheelEventEndTimeout = timeout),
269
- });
270
- }
271
-
272
- async function resize(width, height) {
273
- await performResize(width, height, {
274
- viewerStore,
275
- status,
276
- Status,
277
- genericRenderWindow: genericRenderWindow.value,
278
- viewStream,
279
- remoteRender,
280
- });
281
- }
282
-
283
- function getAverageBrightness(rect) {
284
- return computeAverageBrightness(rect, {
285
- latestImage: latestImage.value,
286
- offscreenCtx,
287
- offscreenCanvas,
288
- genericRenderWindow: genericRenderWindow.value,
289
- });
290
- }
291
-
292
106
  function exportStores() {
293
- const camera = genericRenderWindow.value.getRenderer().getActiveCamera();
294
- return { zScale: zScale.value, camera_options: getCameraOptions(camera) || camera_options };
295
- }
296
-
297
- async function importStores(snapshot) {
298
- await applySnapshot(snapshot, {
299
- genericRenderWindow: genericRenderWindow.value,
300
- setZScaling,
301
- syncRemoteCamera,
302
- setCamera,
303
- });
304
- }
305
-
306
- function clear() {
307
- performClear({ genericRenderWindow: genericRenderWindow.value, hybridDb });
107
+ const renderer = genericRenderWindow.value.getRenderer();
108
+ const camera = renderer.getActiveCamera();
109
+ return {
110
+ zScale: sceneStore.zScale.value,
111
+ camera_options: getCameraOptions(camera) || cameraStore.camera_options,
112
+ };
308
113
  }
309
114
 
310
115
  return {
311
- hybridDb,
312
116
  genericRenderWindow,
313
- addItem,
314
- removeItem,
315
- setVisibility,
316
- setZScaling,
317
- setClippingPlanes,
318
- setShrink,
319
- syncRemoteCamera,
320
- setCamera,
117
+ status,
118
+ is_moving,
119
+ is_picking,
321
120
  initHybridViewer,
322
121
  remoteRender,
323
- resize,
324
- resetCamera,
325
- focusCameraOnObject,
326
- setCameraOrientation,
327
- setContainer,
328
- zScale,
329
- is_picking,
330
- is_hover_highlight,
331
- hover_highlight_field_type,
332
- clearHoverHighlight,
333
- hoverData,
334
- hoverPosition,
335
- clear,
336
122
  exportStores,
337
- importStores,
338
- camera_options,
339
- latestImage,
340
- getAverageBrightness,
123
+ importStores: applySnapshot,
124
+ ...brightnessStore,
125
+ ...sceneStore,
126
+ ...viewportStore,
127
+ ...filtersStore,
128
+ ...highlightStore,
129
+ ...rulerStore,
130
+ ...cameraStore,
341
131
  };
342
132
  });
@@ -1,9 +1,32 @@
1
- import { BACKGROUND_GREY_VALUE, RGB_MAX } from "./hybrid_viewer_constants";
1
+ import { BACKGROUND_GREY_VALUE, RGB_MAX } from "./constants";
2
+ import { useHybridViewerStore } from "@ogw_front/stores/hybrid_viewer";
2
3
 
3
4
  const RGBA_CHANNELS = 4;
4
5
  const SAMPLE_SIZE = 10;
5
6
  const TOTAL_CHANNELS = 400;
6
7
 
8
+ function useHybridViewerBrightness() {
9
+ const latestImage = ref(undefined);
10
+ const offscreenCanvas =
11
+ typeof document === "undefined" ? undefined : document.createElement("canvas");
12
+ const offscreenCtx = offscreenCanvas
13
+ ? offscreenCanvas.getContext("2d", { willReadFrequently: true })
14
+ : undefined;
15
+
16
+ function getAverageBrightness(rect) {
17
+ return computeAverageBrightness(rect, {
18
+ latestImage: latestImage.value,
19
+ offscreenCtx,
20
+ offscreenCanvas,
21
+ });
22
+ }
23
+
24
+ return {
25
+ latestImage,
26
+ getAverageBrightness,
27
+ };
28
+ }
29
+
7
30
  function mapRect(rect, latestImage, canvasRect) {
8
31
  const scaleX = latestImage.width / canvasRect.width;
9
32
  const scaleY = latestImage.height / canvasRect.height;
@@ -16,11 +39,12 @@ function mapRect(rect, latestImage, canvasRect) {
16
39
  }
17
40
 
18
41
  function computeAverageBrightness(rect, options) {
19
- const { latestImage, offscreenCtx, offscreenCanvas, genericRenderWindow } = options;
20
- if (!latestImage || !offscreenCtx || !offscreenCanvas || !genericRenderWindow) {
42
+ const { latestImage, offscreenCtx, offscreenCanvas } = options;
43
+ const { genericRenderWindow } = useHybridViewerStore();
44
+ if (!latestImage || !offscreenCtx || !offscreenCanvas || !genericRenderWindow.value) {
21
45
  return BACKGROUND_GREY_VALUE / RGB_MAX;
22
46
  }
23
- const canvas = genericRenderWindow.getApiSpecificRenderWindow().getCanvas();
47
+ const canvas = genericRenderWindow.value.getApiSpecificRenderWindow().getCanvas();
24
48
  if (!canvas) {
25
49
  return BACKGROUND_GREY_VALUE / RGB_MAX;
26
50
  }
@@ -59,4 +83,4 @@ function computeAverageBrightness(rect, options) {
59
83
  }
60
84
  }
61
85
 
62
- export { computeAverageBrightness };
86
+ export { computeAverageBrightness, useHybridViewerBrightness };