@geode/opengeodeweb-front 10.29.1 → 10.30.0-rc.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/components/ClippingPlaneCard.vue +112 -0
- package/app/components/ClippingPlanes.vue +238 -0
- package/app/components/Launcher.vue +1 -1
- package/app/components/ObjectSelector.vue +26 -76
- package/app/components/ToolPanel.vue +3 -2
- package/app/components/ViewToolbar.vue +12 -0
- package/app/components/Viewer/ObjectTree/Base/TreeRow.vue +58 -20
- package/app/components/Viewer/ObjectTree/Views/GlobalObjects.vue +42 -40
- package/app/composables/clipping_planes_widget.js +262 -0
- package/app/composables/hover_highlight.js +6 -0
- package/app/composables/project_manager.js +14 -11
- package/app/composables/use_overlapping_picker.js +15 -2
- package/app/stores/app.js +56 -11
- package/app/stores/back.js +10 -27
- package/app/stores/cloud.js +7 -1
- package/app/stores/data.js +32 -1
- package/app/stores/data_style.js +11 -0
- package/app/stores/hybrid_viewer.js +13 -5
- package/app/stores/infra.js +2 -7
- package/app/stores/viewer.js +9 -27
- package/app/utils/clipping_planes.js +110 -0
- package/app/utils/extension.js +30 -3
- package/app/utils/import_workflow.js +9 -12
- package/app/utils/log.js +18 -0
- package/app/utils/stores.js +32 -0
- package/internal/stores/hybrid_viewer.js +8 -63
- package/internal/stores/hybrid_viewer_brightness.js +62 -0
- package/internal/utils/api_fetch.js +27 -53
- package/internal/utils/upload_file.js +36 -29
- package/internal/utils/viewer_call.js +7 -2
- package/nuxt.config.js +2 -1
- package/package.json +4 -3
- package/server/api/local/{project_folder_path.post.js → app/project_folder_path.post.js} +1 -1
- package/server/api/local/{run_back.post.js → app/run_back.post.js} +1 -1
- package/server/api/local/{run_viewer.post.js → app/run_viewer.post.js} +1 -1
- package/server/api/{microservice → local}/extensions/upload.put.js +16 -37
- package/server/api/microservice/app/set_app_base_url.post.js +25 -0
- package/server/api/microservice/app/set_back_base_url.post.js +22 -0
- package/server/api/microservice/app/set_viewer_base_url.post.js +22 -0
- package/server/api/microservice/extensions/download.post.js +32 -0
- package/server/api/microservice/extensions/kill.post.js +3 -3
- package/server/api/microservice/extensions/run.post.js +5 -4
- package/server/api/serverless/{run_cloud.js → run_cloud.post.js} +1 -1
- package/{app/utils/config.js → server/utils/app_config.js} +28 -3
- package/{app/utils/local → server/utils}/path.js +1 -1
- package/{app/utils/local → server/utils}/scripts.js +5 -2
- package/server/utils/server_config.js +32 -0
- package/shared/scripts.js +56 -0
- package/shared/utils/fetch_raw.js +61 -0
- package/shared/utils/fetch_schema.js +46 -0
- package/shared/utils/file.js +16 -0
- package/shared/utils/response_handlers/load.js +74 -0
- package/tests/integration/setup.js +3 -3
- package/tests/integration/stores/data_style/mesh/cells.nuxt.test.js +1 -1
- package/tests/integration/stores/data_style/mesh/edges.nuxt.test.js +1 -1
- package/tests/integration/stores/data_style/mesh/index.nuxt.test.js +1 -1
- package/tests/integration/stores/data_style/mesh/points.nuxt.test.js +1 -1
- package/tests/integration/stores/data_style/mesh/polygons.nuxt.test.js +1 -1
- package/tests/integration/stores/data_style/mesh/polyhedra.nuxt.test.js +1 -1
- package/tests/integration/stores/data_style/model/blocks.nuxt.test.js +1 -1
- package/tests/integration/stores/data_style/model/corners.nuxt.test.js +1 -1
- package/tests/integration/stores/data_style/model/edges.nuxt.test.js +1 -1
- package/tests/integration/stores/data_style/model/index.nuxt.test.js +1 -1
- package/tests/integration/stores/data_style/model/lines.nuxt.test.js +1 -1
- package/tests/integration/stores/data_style/model/points.nuxt.test.js +1 -1
- package/tests/integration/stores/data_style/model/surfaces.nuxt.test.js +1 -1
- package/tests/integration/stores/viewer.nuxt.test.js +1 -1
- package/tests/unit/composables/project_manager.nuxt.test.js +14 -2
- package/tests/unit/composables/upload_file.nuxt.test.js +2 -2
- package/tests/unit/stores/back.nuxt.test.js +1 -1
- package/tests/unit/stores/cloud.nuxt.test.js +22 -20
- package/tests/unit/stores/data.nuxt.test.js +35 -0
- package/tests/unit/stores/infra.nuxt.test.js +13 -6
- package/tests/unit/stores/viewer.nuxt.test.js +1 -1
- package/tests/unit/utils/validate_schema.nuxt.test.js +1 -1
- package/tests/vitest.config.js +7 -5
- /package/server/api/local/{kill.post.js → app/kill.post.js} +0 -0
- /package/{app/utils/local → server/utils}/cleanup.js +0 -0
- /package/{app/utils/local → server/utils}/microservices.js +0 -0
- /package/{app → server}/utils/server.js +0 -0
- /package/{app/utils/local → shared}/app_mode.js +0 -0
- /package/{app → shared}/utils/validate_schema.js +0 -0
package/app/stores/cloud.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { Status } from "@ogw_front/utils/status";
|
|
2
|
+
import { fetchSchema } from "@ogw_shared/utils/fetch_schema";
|
|
3
|
+
import { setAppBaseUrl } from "@ogw_shared/scripts";
|
|
2
4
|
import { useAppStore } from "./app";
|
|
3
5
|
import { useFeedbackStore } from "./feedback";
|
|
4
6
|
import { useInfraStore } from "./infra";
|
|
@@ -25,7 +27,7 @@ export const useCloudStore = defineStore("cloud", {
|
|
|
25
27
|
console.log("[CLOUD] params", params);
|
|
26
28
|
const appStore = useAppStore();
|
|
27
29
|
const feedbackStore = useFeedbackStore();
|
|
28
|
-
return
|
|
30
|
+
return fetchSchema(
|
|
29
31
|
{ schema, params },
|
|
30
32
|
{
|
|
31
33
|
request_error_function: () => {
|
|
@@ -40,6 +42,10 @@ export const useCloudStore = defineStore("cloud", {
|
|
|
40
42
|
infraStore.$patch({
|
|
41
43
|
domain_name: response.url,
|
|
42
44
|
});
|
|
45
|
+
setAppBaseUrl(appStore.base_url);
|
|
46
|
+
appStore.$patch({
|
|
47
|
+
projectFolderPath: "/project",
|
|
48
|
+
});
|
|
43
49
|
},
|
|
44
50
|
response_error_function: () => {
|
|
45
51
|
feedbackStore.$patch({ server_error: true });
|
package/app/stores/data.js
CHANGED
|
@@ -11,6 +11,33 @@ import { useViewerStore } from "@ogw_front/stores/viewer";
|
|
|
11
11
|
|
|
12
12
|
const viewer_generic_schemas = viewer_schemas.opengeodeweb_viewer.generic;
|
|
13
13
|
|
|
14
|
+
function checkItemViewable(item) {
|
|
15
|
+
if (!item || typeof item !== "object") {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
if (item.is_viewable !== undefined) {
|
|
19
|
+
return Boolean(item.is_viewable);
|
|
20
|
+
}
|
|
21
|
+
if (item.binary_light_viewable !== undefined) {
|
|
22
|
+
return item.binary_light_viewable !== "not_viewable";
|
|
23
|
+
}
|
|
24
|
+
if (
|
|
25
|
+
item.geode_object_type === "HorizonStack3D" ||
|
|
26
|
+
item.id === "HorizonStack3D" ||
|
|
27
|
+
item.title === "HorizonStack3D"
|
|
28
|
+
) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function isItemViewable(itemOrId) {
|
|
35
|
+
if (typeof itemOrId === "string") {
|
|
36
|
+
return database.data.get(itemOrId).then(checkItemViewable);
|
|
37
|
+
}
|
|
38
|
+
return checkItemViewable(itemOrId);
|
|
39
|
+
}
|
|
40
|
+
|
|
14
41
|
// oxlint-disable-next-line max-lines-per-function, max-statements
|
|
15
42
|
export const useDataStore = defineStore("data", () => {
|
|
16
43
|
const viewerStore = useViewerStore();
|
|
@@ -93,8 +120,11 @@ export const useDataStore = defineStore("data", () => {
|
|
|
93
120
|
geode_object_type: new_item.geode_object_type,
|
|
94
121
|
visible: true,
|
|
95
122
|
created_at: new Date().toISOString(),
|
|
96
|
-
|
|
123
|
+
is_viewable: new_item.is_viewable,
|
|
97
124
|
};
|
|
125
|
+
if (new_item.binary_light_viewable !== undefined && new_item.binary_light_viewable !== null) {
|
|
126
|
+
itemData.binary_light_viewable = new_item.binary_light_viewable;
|
|
127
|
+
}
|
|
98
128
|
return data_db.put(itemData);
|
|
99
129
|
}
|
|
100
130
|
|
|
@@ -213,6 +243,7 @@ export const useDataStore = defineStore("data", () => {
|
|
|
213
243
|
item,
|
|
214
244
|
allItems,
|
|
215
245
|
refItem,
|
|
246
|
+
isItemViewable,
|
|
216
247
|
meshComponentType,
|
|
217
248
|
registerObject,
|
|
218
249
|
deregisterObject,
|
package/app/stores/data_style.js
CHANGED
|
@@ -21,6 +21,10 @@ export const useDataStyleStore = defineStore("dataStyle", () => {
|
|
|
21
21
|
|
|
22
22
|
async function setVisibility(id, visibility) {
|
|
23
23
|
const item = await dataStore.item(id);
|
|
24
|
+
if (!(await dataStore.isItemViewable(item))) {
|
|
25
|
+
return dataStyleState.mutateStyle(id, { visibility });
|
|
26
|
+
}
|
|
27
|
+
|
|
24
28
|
const { viewer_type } = item;
|
|
25
29
|
|
|
26
30
|
if (viewer_type === "mesh") {
|
|
@@ -34,6 +38,10 @@ export const useDataStyleStore = defineStore("dataStyle", () => {
|
|
|
34
38
|
|
|
35
39
|
async function applyDefaultStyle(id) {
|
|
36
40
|
const item = await dataStore.item(id);
|
|
41
|
+
if (!(await dataStore.isItemViewable(item))) {
|
|
42
|
+
throw new Error(`applyDefaultStyle called for non-viewable item: ${id}`);
|
|
43
|
+
}
|
|
44
|
+
|
|
37
45
|
const { viewer_type } = item;
|
|
38
46
|
|
|
39
47
|
if (viewer_type === "mesh") {
|
|
@@ -81,6 +89,9 @@ export const useDataStyleStore = defineStore("dataStyle", () => {
|
|
|
81
89
|
const ids = Object.keys(dataStyleState.styles.value);
|
|
82
90
|
const promises = ids.map(async (id) => {
|
|
83
91
|
const meta = await dataStore.item(id);
|
|
92
|
+
if (!(await dataStore.isItemViewable(meta))) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
84
95
|
const viewerType = meta.viewer_type;
|
|
85
96
|
if (viewerType === "mesh") {
|
|
86
97
|
return meshStyleStore.applyMeshStyle(id);
|
|
@@ -12,7 +12,10 @@ import {
|
|
|
12
12
|
performSyncRemoteCamera,
|
|
13
13
|
} from "@ogw_internal/stores/hybrid_viewer_camera";
|
|
14
14
|
import {
|
|
15
|
-
|
|
15
|
+
createClearHoverData,
|
|
16
|
+
createHoverHighlight,
|
|
17
|
+
} from "@ogw_internal/stores/hybrid_viewer_highlight";
|
|
18
|
+
import {
|
|
16
19
|
performAddItem,
|
|
17
20
|
performClear,
|
|
18
21
|
performClearHoverHighlight,
|
|
@@ -22,10 +25,7 @@ import {
|
|
|
22
25
|
performSetVisibility,
|
|
23
26
|
performSetZScaling,
|
|
24
27
|
} from "@ogw_internal/stores/hybrid_viewer";
|
|
25
|
-
import {
|
|
26
|
-
createClearHoverData,
|
|
27
|
-
createHoverHighlight,
|
|
28
|
-
} from "@ogw_internal/stores/hybrid_viewer_highlight";
|
|
28
|
+
import { computeAverageBrightness } from "@ogw_internal/stores/hybrid_viewer_brightness";
|
|
29
29
|
import { newInstance as vtkActor } from "@kitware/vtk.js/Rendering/Core/Actor";
|
|
30
30
|
import { newInstance as vtkGenericRenderWindow } from "@kitware/vtk.js/Rendering/Misc/GenericRenderWindow";
|
|
31
31
|
import { newInstance as vtkMapper } from "@kitware/vtk.js/Rendering/Core/Mapper";
|
|
@@ -137,6 +137,13 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => {
|
|
|
137
137
|
});
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
async function setClippingPlanes(ids, planes) {
|
|
141
|
+
const schema = viewer_schemas.opengeodeweb_viewer.viewer.clipping_planes;
|
|
142
|
+
const params = { ids, planes };
|
|
143
|
+
await viewerStore.request({ schema, params });
|
|
144
|
+
await remoteRender();
|
|
145
|
+
}
|
|
146
|
+
|
|
140
147
|
function resetCamera() {
|
|
141
148
|
genericRenderWindow.value.getRenderer().resetCamera();
|
|
142
149
|
genericRenderWindow.value.getRenderWindow().render();
|
|
@@ -307,6 +314,7 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => {
|
|
|
307
314
|
removeItem,
|
|
308
315
|
setVisibility,
|
|
309
316
|
setZScaling,
|
|
317
|
+
setClippingPlanes,
|
|
310
318
|
syncRemoteCamera,
|
|
311
319
|
setCamera,
|
|
312
320
|
initHybridViewer,
|
package/app/stores/infra.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Status } from "@ogw_front/utils/status";
|
|
2
|
-
import { appMode } from "@
|
|
2
|
+
import { appMode } from "@ogw_shared/app_mode";
|
|
3
3
|
import { registerRunningExtensions } from "@ogw_front/utils/extension";
|
|
4
4
|
import { useAppStore } from "@ogw_front/stores/app";
|
|
5
5
|
import { useCloudStore } from "@ogw_front/stores/cloud";
|
|
@@ -9,7 +9,7 @@ export const useInfraStore = defineStore("infra", {
|
|
|
9
9
|
app_mode: useRuntimeConfig().public.MODE,
|
|
10
10
|
status: Status.NOT_CREATED,
|
|
11
11
|
microservices: [],
|
|
12
|
-
domain_name:
|
|
12
|
+
domain_name: globalThis.location.hostname,
|
|
13
13
|
}),
|
|
14
14
|
getters: {
|
|
15
15
|
microservices_connected() {
|
|
@@ -77,11 +77,6 @@ export const useInfraStore = defineStore("infra", {
|
|
|
77
77
|
},
|
|
78
78
|
async create_connection() {
|
|
79
79
|
console.log("[INFRA] Starting create_connection");
|
|
80
|
-
console.log(
|
|
81
|
-
"[INFRA] Connecting microservices:",
|
|
82
|
-
this.microservices.map((store) => store.$id),
|
|
83
|
-
);
|
|
84
|
-
|
|
85
80
|
await Promise.all(
|
|
86
81
|
this.microservices.map(async (store) => {
|
|
87
82
|
console.log("[INFRA] Connecting to microservice:", store.$id);
|
package/app/stores/viewer.js
CHANGED
|
@@ -8,8 +8,12 @@ import { connectImageStream } from "@kitware/vtk.js/Rendering/Misc/RemoteView";
|
|
|
8
8
|
import schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json";
|
|
9
9
|
|
|
10
10
|
// Local imports
|
|
11
|
+
import {
|
|
12
|
+
getWebsocketApiPort,
|
|
13
|
+
getWebsocketApiProtocol,
|
|
14
|
+
isCloudMode,
|
|
15
|
+
} from "@ogw_front/utils/stores.js";
|
|
11
16
|
import { Status } from "@ogw_front/utils/status";
|
|
12
|
-
import { appMode } from "@ogw_front/utils/local/app_mode";
|
|
13
17
|
import { useAppStore } from "@ogw_front/stores/app";
|
|
14
18
|
import { useInfraStore } from "@ogw_front/stores/infra";
|
|
15
19
|
import { viewer_call } from "@ogw_internal/utils/viewer_call";
|
|
@@ -33,23 +37,13 @@ export const useViewerStore = defineStore(
|
|
|
33
37
|
const version = ref("0.0.0");
|
|
34
38
|
const busy = ref(0);
|
|
35
39
|
|
|
36
|
-
const protocol = computed(() =>
|
|
37
|
-
if (infraStore.app_mode === appMode.CLOUD) {
|
|
38
|
-
return "wss";
|
|
39
|
-
}
|
|
40
|
-
return "ws";
|
|
41
|
-
});
|
|
40
|
+
const protocol = computed(() => getWebsocketApiProtocol());
|
|
42
41
|
|
|
43
|
-
const port = computed(() =>
|
|
44
|
-
if (infraStore.app_mode === appMode.CLOUD) {
|
|
45
|
-
return "443";
|
|
46
|
-
}
|
|
47
|
-
return default_local_port.value;
|
|
48
|
-
});
|
|
42
|
+
const port = computed(() => getWebsocketApiPort(default_local_port.value));
|
|
49
43
|
|
|
50
44
|
const base_url = computed(() => {
|
|
51
45
|
let viewer_url = `${protocol.value}://${infraStore.domain_name}:${port.value}`;
|
|
52
|
-
if (
|
|
46
|
+
if (isCloudMode()) {
|
|
53
47
|
viewer_url += `/viewer`;
|
|
54
48
|
}
|
|
55
49
|
viewer_url += "/ws";
|
|
@@ -133,7 +127,7 @@ export const useViewerStore = defineStore(
|
|
|
133
127
|
|
|
134
128
|
const { COMMAND_VIEWER, NUXT_ROOT_PATH } = useRuntimeConfig().public;
|
|
135
129
|
const schema = {
|
|
136
|
-
$id: "/api/local/run_viewer",
|
|
130
|
+
$id: "/api/local/app/run_viewer",
|
|
137
131
|
methods: ["POST"],
|
|
138
132
|
type: "object",
|
|
139
133
|
properties: { COMMAND_VIEWER: { type: "string" }, NUXT_ROOT_PATH: { type: "string" } },
|
|
@@ -162,25 +156,13 @@ export const useViewerStore = defineStore(
|
|
|
162
156
|
}
|
|
163
157
|
|
|
164
158
|
function request({ schema, params = {}, timeout = request_timeout }, callbacks = {}) {
|
|
165
|
-
console.log("[VIEWER] Request:", schema.$id);
|
|
166
|
-
const start = Date.now();
|
|
167
|
-
|
|
168
|
-
// Get current store instance to pass to viewer_call
|
|
169
159
|
const store = useViewerStore();
|
|
170
|
-
|
|
171
160
|
return viewer_call(
|
|
172
161
|
store,
|
|
173
162
|
{ schema, params, timeout },
|
|
174
163
|
{
|
|
175
164
|
...callbacks,
|
|
176
165
|
response_function: async (response) => {
|
|
177
|
-
console.log(
|
|
178
|
-
"[VIEWER] Request completed:",
|
|
179
|
-
schema.$id,
|
|
180
|
-
"in",
|
|
181
|
-
(Date.now() - start) / MS_PER_SECOND,
|
|
182
|
-
"s",
|
|
183
|
-
);
|
|
184
166
|
if (callbacks.response_function) {
|
|
185
167
|
await callbacks.response_function(response);
|
|
186
168
|
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
const AXIS_SCALE = 0.45;
|
|
2
|
+
const SIZE_RATIO = 0.1;
|
|
3
|
+
const DEBOUNCE_DELAY = 200;
|
|
4
|
+
const CHANGE_THRESHOLD = 1e-4;
|
|
5
|
+
// oxlint-disable no-magic-numbers
|
|
6
|
+
const COLOR_BLUE = [0.12, 0.53, 0.9];
|
|
7
|
+
const COLOR_GREEN = [0.26, 0.63, 0.28];
|
|
8
|
+
const COLOR_ORANGE = [0.98, 0.55, 0];
|
|
9
|
+
const COLOR_PURPLE = [0.55, 0.14, 0.67];
|
|
10
|
+
const COLOR_RED = [0.9, 0.22, 0.21];
|
|
11
|
+
// oxlint-enable no-magic-numbers
|
|
12
|
+
const PLANE_COLORS = [COLOR_BLUE, COLOR_GREEN, COLOR_ORANGE, COLOR_PURPLE, COLOR_RED];
|
|
13
|
+
const NORMAL_X = [1, 0, 0];
|
|
14
|
+
const NORMAL_Y = [0, 1, 0];
|
|
15
|
+
const NORMAL_Z = [0, 0, 1];
|
|
16
|
+
const DEFAULT_NORMALS = [NORMAL_X, NORMAL_Y, NORMAL_Z];
|
|
17
|
+
const RGB_MAX_VALUE = 255;
|
|
18
|
+
|
|
19
|
+
function getPlaneCssColor(index) {
|
|
20
|
+
const rgb = PLANE_COLORS[index % PLANE_COLORS.length];
|
|
21
|
+
return `rgb(${rgb.map((channel) => Math.round(channel * RGB_MAX_VALUE)).join(",")})`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function hasPlaneChanged(origin, normal, currentOrigin, currentNormal) {
|
|
25
|
+
if (!currentOrigin) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
return (
|
|
29
|
+
origin.some((val, idx) => Math.abs(val - currentOrigin[idx]) > CHANGE_THRESHOLD) ||
|
|
30
|
+
normal.some((val, idx) => Math.abs(val - currentNormal[idx]) > CHANGE_THRESHOLD)
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function getPlaneStyle(rgb) {
|
|
35
|
+
return {
|
|
36
|
+
active: {
|
|
37
|
+
plane: { opacity: 1, color: rgb },
|
|
38
|
+
normal: { opacity: 1, color: rgb },
|
|
39
|
+
origin: { opacity: 1, color: rgb },
|
|
40
|
+
},
|
|
41
|
+
inactive: {
|
|
42
|
+
plane: { opacity: 0.5, color: rgb },
|
|
43
|
+
normal: { opacity: 1, color: rgb },
|
|
44
|
+
origin: { opacity: 1, color: rgb },
|
|
45
|
+
},
|
|
46
|
+
static: {
|
|
47
|
+
display2D: { representation: 0 },
|
|
48
|
+
outline: { color: [1, 1, 1], opacity: 1, representation: 1, interpolation: 0 },
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function computeSceneBounds(actors) {
|
|
54
|
+
let bounds = [Infinity, -Infinity, Infinity, -Infinity, Infinity, -Infinity];
|
|
55
|
+
for (const actor of actors) {
|
|
56
|
+
const boundsOfActor = actor.getBounds();
|
|
57
|
+
bounds = [
|
|
58
|
+
Math.min(bounds[0], boundsOfActor[0]),
|
|
59
|
+
Math.max(bounds[1], boundsOfActor[1]),
|
|
60
|
+
Math.min(bounds[2], boundsOfActor[2]),
|
|
61
|
+
Math.max(bounds[3], boundsOfActor[3]),
|
|
62
|
+
Math.min(bounds[4], boundsOfActor[4]),
|
|
63
|
+
Math.max(bounds[5], boundsOfActor[5]),
|
|
64
|
+
];
|
|
65
|
+
}
|
|
66
|
+
return bounds;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function computeSceneBoundsInfo(actors) {
|
|
70
|
+
if (!actors || actors.length === 0) {
|
|
71
|
+
return { center: [0, 0, 0], cubicBounds: [-1, 1, -1, 1, -1, 1] };
|
|
72
|
+
}
|
|
73
|
+
const [xmin, xmax, ymin, ymax, zmin, zmax] = computeSceneBounds(actors);
|
|
74
|
+
const center = [
|
|
75
|
+
Number(((xmin + xmax) / 2).toFixed(4)),
|
|
76
|
+
Number(((ymin + ymax) / 2).toFixed(4)),
|
|
77
|
+
Number(((zmin + zmax) / 2).toFixed(4)),
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
const maxActorExtent = Math.max(
|
|
81
|
+
...actors.map((actor) => {
|
|
82
|
+
const bounds = actor.getBounds();
|
|
83
|
+
return Math.max(bounds[1] - bounds[0], bounds[3] - bounds[2], bounds[5] - bounds[4]);
|
|
84
|
+
}),
|
|
85
|
+
);
|
|
86
|
+
const halfExtent = maxActorExtent / 2;
|
|
87
|
+
const cubicBounds = [
|
|
88
|
+
center[0] - halfExtent,
|
|
89
|
+
center[0] + halfExtent,
|
|
90
|
+
center[1] - halfExtent,
|
|
91
|
+
center[1] + halfExtent,
|
|
92
|
+
center[2] - halfExtent,
|
|
93
|
+
center[2] + halfExtent,
|
|
94
|
+
];
|
|
95
|
+
return { center, cubicBounds };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export {
|
|
99
|
+
AXIS_SCALE,
|
|
100
|
+
SIZE_RATIO,
|
|
101
|
+
DEBOUNCE_DELAY,
|
|
102
|
+
CHANGE_THRESHOLD,
|
|
103
|
+
PLANE_COLORS,
|
|
104
|
+
DEFAULT_NORMALS,
|
|
105
|
+
getPlaneCssColor,
|
|
106
|
+
hasPlaneChanged,
|
|
107
|
+
getPlaneStyle,
|
|
108
|
+
computeSceneBounds,
|
|
109
|
+
computeSceneBoundsInfo,
|
|
110
|
+
};
|
package/app/utils/extension.js
CHANGED
|
@@ -12,6 +12,11 @@ async function importExtensionFile(file) {
|
|
|
12
12
|
return registerRunningExtensions();
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
async function importExtensionURL(url) {
|
|
16
|
+
await downloadExtension(url);
|
|
17
|
+
return registerRunningExtensions();
|
|
18
|
+
}
|
|
19
|
+
|
|
15
20
|
async function registerRunningExtensions() {
|
|
16
21
|
const appStore = useAppStore();
|
|
17
22
|
const infraStore = useInfraStore();
|
|
@@ -74,6 +79,27 @@ async function uploadExtension(file) {
|
|
|
74
79
|
await appStore.upload(file);
|
|
75
80
|
}
|
|
76
81
|
|
|
82
|
+
function downloadExtension({ url, extensionFileName }) {
|
|
83
|
+
const appStore = useAppStore();
|
|
84
|
+
const { PROJECT: projectName } = useRuntimeConfig().public;
|
|
85
|
+
const params = { projectName, url, extensionFileName };
|
|
86
|
+
|
|
87
|
+
const schema = {
|
|
88
|
+
$id: "/api/microservice/extensions/download",
|
|
89
|
+
methods: ["POST"],
|
|
90
|
+
type: "object",
|
|
91
|
+
properties: {
|
|
92
|
+
extensionFileName: { type: "string" },
|
|
93
|
+
projectName: { type: "string" },
|
|
94
|
+
url: { type: "string" },
|
|
95
|
+
},
|
|
96
|
+
required: ["extensionFileName", "projectName", "url"],
|
|
97
|
+
additionalProperties: false,
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
return appStore.request({ schema, params });
|
|
101
|
+
}
|
|
102
|
+
|
|
77
103
|
function runExtensions() {
|
|
78
104
|
const appStore = useAppStore();
|
|
79
105
|
const { projectFolderPath } = appStore;
|
|
@@ -121,9 +147,10 @@ function killExtension(extensionId) {
|
|
|
121
147
|
|
|
122
148
|
export {
|
|
123
149
|
importExtensionFile,
|
|
124
|
-
|
|
125
|
-
|
|
150
|
+
importExtensionURL,
|
|
151
|
+
killExtension,
|
|
126
152
|
registerRunningExtensions,
|
|
127
153
|
runExtensions,
|
|
128
|
-
|
|
154
|
+
unloadExtension,
|
|
155
|
+
uploadExtension,
|
|
129
156
|
};
|
|
@@ -35,13 +35,6 @@ async function importWorkflow(files) {
|
|
|
35
35
|
return results;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
function buildImportItemFromPayloadApi(value, geode_object_type) {
|
|
39
|
-
return {
|
|
40
|
-
geode_object_type,
|
|
41
|
-
...value,
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
38
|
async function importItem(item) {
|
|
46
39
|
const dataStore = useDataStore();
|
|
47
40
|
const dataStyleStore = useDataStyleStore();
|
|
@@ -61,14 +54,19 @@ async function importItem(item) {
|
|
|
61
54
|
item.viewer_type === "model" ? dataStore.addComponentRelations(item) : Promise.resolve();
|
|
62
55
|
treeviewStore.addItem(item.geode_object_type, item.name, item.id, item.viewer_type);
|
|
63
56
|
const addDataStyleTask = dataStyleStore.addDataStyle(item.id, item.geode_object_type);
|
|
64
|
-
const addViewerTask = addDataTask.then(() => {
|
|
65
|
-
if (item
|
|
57
|
+
const addViewerTask = addDataTask.then(async () => {
|
|
58
|
+
if (!(await dataStore.isItemViewable(item))) {
|
|
66
59
|
return;
|
|
67
60
|
}
|
|
68
61
|
return hybridViewerStore.addItem(item.id);
|
|
69
62
|
});
|
|
70
63
|
const applyStyleTask = Promise.all([registerTask, addDataComponentsTask, addDataStyleTask]).then(
|
|
71
|
-
() =>
|
|
64
|
+
async () => {
|
|
65
|
+
if (await dataStore.isItemViewable(item)) {
|
|
66
|
+
return dataStyleStore.applyDefaultStyle(item.id);
|
|
67
|
+
}
|
|
68
|
+
return undefined;
|
|
69
|
+
},
|
|
72
70
|
);
|
|
73
71
|
await Promise.all([
|
|
74
72
|
registerTask,
|
|
@@ -89,8 +87,7 @@ async function importFile(filename, geode_object_type) {
|
|
|
89
87
|
filename,
|
|
90
88
|
};
|
|
91
89
|
const response = await backStore.request({ schema, params });
|
|
92
|
-
|
|
93
|
-
return importItem(item);
|
|
90
|
+
return importItem(response);
|
|
94
91
|
}
|
|
95
92
|
|
|
96
93
|
async function importWorkflowFromSnapshot(items) {
|
package/app/utils/log.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
function startRequestLog(microservice, schema) {
|
|
2
|
+
console.log(`[${microservice.$id}] Request:`, schema.$id);
|
|
3
|
+
const requestStartingTime = new Date(Date.now());
|
|
4
|
+
return requestStartingTime;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function endRequestLog(microservice, schema, requestStartingTime) {
|
|
8
|
+
const requestEndingTime = new Date(Date.now());
|
|
9
|
+
console.log(
|
|
10
|
+
`[${microservice.$id}] Request completed:`,
|
|
11
|
+
schema.$id,
|
|
12
|
+
"in",
|
|
13
|
+
requestEndingTime.getSeconds() - requestStartingTime.getSeconds(),
|
|
14
|
+
"s",
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { startRequestLog, endRequestLog };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { appMode } from "@ogw_shared/app_mode";
|
|
2
|
+
import { useInfraStore } from "@ogw_front/stores/infra";
|
|
3
|
+
|
|
4
|
+
function isCloudMode() {
|
|
5
|
+
const infraStore = useInfraStore();
|
|
6
|
+
return infraStore.app_mode === appMode.CLOUD;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function getRestApiProtocol() {
|
|
10
|
+
const protocol = isCloudMode() ? "https" : "http";
|
|
11
|
+
return protocol;
|
|
12
|
+
}
|
|
13
|
+
function getRestApiPort(defaultLocalPort) {
|
|
14
|
+
const port = isCloudMode() ? "443" : defaultLocalPort;
|
|
15
|
+
return port;
|
|
16
|
+
}
|
|
17
|
+
function getWebsocketApiProtocol() {
|
|
18
|
+
const protocol = isCloudMode() ? "wss" : "ws";
|
|
19
|
+
return protocol;
|
|
20
|
+
}
|
|
21
|
+
function getWebsocketApiPort(defaultLocalPort) {
|
|
22
|
+
const port = isCloudMode() ? "443" : defaultLocalPort;
|
|
23
|
+
return port;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export {
|
|
27
|
+
isCloudMode,
|
|
28
|
+
getRestApiPort,
|
|
29
|
+
getRestApiProtocol,
|
|
30
|
+
getWebsocketApiProtocol,
|
|
31
|
+
getWebsocketApiPort,
|
|
32
|
+
};
|
|
@@ -1,65 +1,5 @@
|
|
|
1
|
-
import { BACKGROUND_GREY_VALUE, RGB_MAX } from "./hybrid_viewer_constants";
|
|
2
1
|
import { centerCameraOnPosition } from "./hybrid_viewer_camera";
|
|
3
2
|
|
|
4
|
-
const RGBA_CHANNELS = 4;
|
|
5
|
-
const SAMPLE_SIZE = 10;
|
|
6
|
-
const TOTAL_CHANNELS = 400;
|
|
7
|
-
|
|
8
|
-
function mapRect(rect, latestImage, canvasRect) {
|
|
9
|
-
const scaleX = latestImage.width / canvasRect.width;
|
|
10
|
-
const scaleY = latestImage.height / canvasRect.height;
|
|
11
|
-
return {
|
|
12
|
-
relX: (rect.x - canvasRect.left) * scaleX,
|
|
13
|
-
relY: (rect.y - canvasRect.top) * scaleY,
|
|
14
|
-
relW: rect.width * scaleX,
|
|
15
|
-
relH: rect.height * scaleY,
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function computeAverageBrightness(rect, options) {
|
|
20
|
-
const { latestImage, offscreenCtx, offscreenCanvas, genericRenderWindow } = options;
|
|
21
|
-
if (!latestImage || !offscreenCtx || !offscreenCanvas || !genericRenderWindow) {
|
|
22
|
-
return BACKGROUND_GREY_VALUE / RGB_MAX;
|
|
23
|
-
}
|
|
24
|
-
const canvas = genericRenderWindow.getApiSpecificRenderWindow().getCanvas();
|
|
25
|
-
if (!canvas) {
|
|
26
|
-
return BACKGROUND_GREY_VALUE / RGB_MAX;
|
|
27
|
-
}
|
|
28
|
-
if (rect.width <= 0 || rect.height <= 0) {
|
|
29
|
-
return BACKGROUND_GREY_VALUE / RGB_MAX;
|
|
30
|
-
}
|
|
31
|
-
const { relX, relY, relW, relH } = mapRect(rect, latestImage, canvas.getBoundingClientRect());
|
|
32
|
-
if (relW <= 0 || relH <= 0) {
|
|
33
|
-
return BACKGROUND_GREY_VALUE / RGB_MAX;
|
|
34
|
-
}
|
|
35
|
-
offscreenCanvas.width = SAMPLE_SIZE;
|
|
36
|
-
offscreenCanvas.height = SAMPLE_SIZE;
|
|
37
|
-
try {
|
|
38
|
-
offscreenCtx.drawImage(
|
|
39
|
-
latestImage,
|
|
40
|
-
Math.max(0, relX),
|
|
41
|
-
Math.max(0, relY),
|
|
42
|
-
Math.min(latestImage.width, relW),
|
|
43
|
-
Math.min(latestImage.height, relH),
|
|
44
|
-
0,
|
|
45
|
-
0,
|
|
46
|
-
SAMPLE_SIZE,
|
|
47
|
-
SAMPLE_SIZE,
|
|
48
|
-
);
|
|
49
|
-
const { data } = offscreenCtx.getImageData(0, 0, SAMPLE_SIZE, SAMPLE_SIZE);
|
|
50
|
-
let minBrightness = 1;
|
|
51
|
-
for (let i = 0; i < TOTAL_CHANNELS; i += RGBA_CHANNELS) {
|
|
52
|
-
const brightness = (data[i] + data[i + 1] + data[i + 2]) / (3 * RGB_MAX);
|
|
53
|
-
if (brightness < minBrightness) {
|
|
54
|
-
minBrightness = brightness;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
return minBrightness;
|
|
58
|
-
} catch {
|
|
59
|
-
return BACKGROUND_GREY_VALUE / RGB_MAX;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
3
|
function performClickPicking(event, options) {
|
|
64
4
|
const { container, viewerStore, viewer_schemas, genericRenderWindow, syncRemoteCamera } = options;
|
|
65
5
|
const rect = container.getBoundingClientRect();
|
|
@@ -148,8 +88,11 @@ async function performAddItem(id, options) {
|
|
|
148
88
|
if (!genericRenderWindow) {
|
|
149
89
|
return;
|
|
150
90
|
}
|
|
151
|
-
const
|
|
152
|
-
|
|
91
|
+
const value = await dataStore.item(id);
|
|
92
|
+
if (value && !dataStore.isItemViewable(value)) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const reader = vtkXMLPolyDataReader();
|
|
153
96
|
await reader.parseAsArrayBuffer(new TextEncoder().encode(value.binary_light_viewable));
|
|
154
97
|
const actor = vtkActor(),
|
|
155
98
|
mapper = vtkMapper(),
|
|
@@ -158,6 +101,9 @@ async function performAddItem(id, options) {
|
|
|
158
101
|
actor.getProperty().setColor(actorColor);
|
|
159
102
|
actor.setMapper(mapper);
|
|
160
103
|
const renderer = genericRenderWindow.getRenderer();
|
|
104
|
+
if (hybridDb[id] && hybridDb[id].actor) {
|
|
105
|
+
renderer.removeActor(hybridDb[id].actor);
|
|
106
|
+
}
|
|
161
107
|
const isFirst = renderer.getActors().length === 0;
|
|
162
108
|
renderer.addActor(actor);
|
|
163
109
|
if (isFirst) {
|
|
@@ -307,7 +253,6 @@ function performClear(options) {
|
|
|
307
253
|
}
|
|
308
254
|
|
|
309
255
|
export {
|
|
310
|
-
computeAverageBrightness,
|
|
311
256
|
performAddItem,
|
|
312
257
|
performClearHoverHighlight,
|
|
313
258
|
performClickPicking,
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { BACKGROUND_GREY_VALUE, RGB_MAX } from "./hybrid_viewer_constants";
|
|
2
|
+
|
|
3
|
+
const RGBA_CHANNELS = 4;
|
|
4
|
+
const SAMPLE_SIZE = 10;
|
|
5
|
+
const TOTAL_CHANNELS = 400;
|
|
6
|
+
|
|
7
|
+
function mapRect(rect, latestImage, canvasRect) {
|
|
8
|
+
const scaleX = latestImage.width / canvasRect.width;
|
|
9
|
+
const scaleY = latestImage.height / canvasRect.height;
|
|
10
|
+
return {
|
|
11
|
+
relX: (rect.x - canvasRect.left) * scaleX,
|
|
12
|
+
relY: (rect.y - canvasRect.top) * scaleY,
|
|
13
|
+
relW: rect.width * scaleX,
|
|
14
|
+
relH: rect.height * scaleY,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function computeAverageBrightness(rect, options) {
|
|
19
|
+
const { latestImage, offscreenCtx, offscreenCanvas, genericRenderWindow } = options;
|
|
20
|
+
if (!latestImage || !offscreenCtx || !offscreenCanvas || !genericRenderWindow) {
|
|
21
|
+
return BACKGROUND_GREY_VALUE / RGB_MAX;
|
|
22
|
+
}
|
|
23
|
+
const canvas = genericRenderWindow.getApiSpecificRenderWindow().getCanvas();
|
|
24
|
+
if (!canvas) {
|
|
25
|
+
return BACKGROUND_GREY_VALUE / RGB_MAX;
|
|
26
|
+
}
|
|
27
|
+
if (rect.width <= 0 || rect.height <= 0) {
|
|
28
|
+
return BACKGROUND_GREY_VALUE / RGB_MAX;
|
|
29
|
+
}
|
|
30
|
+
const { relX, relY, relW, relH } = mapRect(rect, latestImage, canvas.getBoundingClientRect());
|
|
31
|
+
if (relW <= 0 || relH <= 0) {
|
|
32
|
+
return BACKGROUND_GREY_VALUE / RGB_MAX;
|
|
33
|
+
}
|
|
34
|
+
offscreenCanvas.width = SAMPLE_SIZE;
|
|
35
|
+
offscreenCanvas.height = SAMPLE_SIZE;
|
|
36
|
+
try {
|
|
37
|
+
offscreenCtx.drawImage(
|
|
38
|
+
latestImage,
|
|
39
|
+
Math.max(0, relX),
|
|
40
|
+
Math.max(0, relY),
|
|
41
|
+
Math.min(latestImage.width, relW),
|
|
42
|
+
Math.min(latestImage.height, relH),
|
|
43
|
+
0,
|
|
44
|
+
0,
|
|
45
|
+
SAMPLE_SIZE,
|
|
46
|
+
SAMPLE_SIZE,
|
|
47
|
+
);
|
|
48
|
+
const { data } = offscreenCtx.getImageData(0, 0, SAMPLE_SIZE, SAMPLE_SIZE);
|
|
49
|
+
let minBrightness = 1;
|
|
50
|
+
for (let i = 0; i < TOTAL_CHANNELS; i += RGBA_CHANNELS) {
|
|
51
|
+
const brightness = (data[i] + data[i + 1] + data[i + 2]) / (3 * RGB_MAX);
|
|
52
|
+
if (brightness < minBrightness) {
|
|
53
|
+
minBrightness = brightness;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return minBrightness;
|
|
57
|
+
} catch {
|
|
58
|
+
return BACKGROUND_GREY_VALUE / RGB_MAX;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export { computeAverageBrightness };
|