Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
f8e423b
proper all files oxlint passed
SpliiT Jul 13, 2026
8b600ed
Apply prepare changes
SpliiT Jul 13, 2026
8650307
feat(HorizonStack): Add Horizon Stack to the horizon step
SpliiT Jul 13, 2026
ea3c5e6
Merge branch 'feat/HorizonStack' of https://github.com/Geode-solution…
SpliiT Jul 13, 2026
3824442
try with more timeout value
SpliiT Jul 13, 2026
2c96cbd
rm comments
SpliiT Jul 15, 2026
05b7129
conditions convention
SpliiT Jul 15, 2026
876d2bb
fix somethings
SpliiT Jul 16, 2026
79eb94b
Merge branch 'next' of https://github.com/Geode-solutions/OpenGeodeWe…
SpliiT Jul 17, 2026
0d787db
Merge branch 'next' of https://github.com/Geode-solutions/OpenGeodeWe…
SpliiT Jul 23, 2026
324ff80
clear logs
SpliiT Jul 24, 2026
12f6de2
clean undefined
SpliiT Jul 24, 2026
561281d
tigger tests
SpliiT Jul 24, 2026
9e43b9b
trigger tests
SpliiT Jul 24, 2026
25ca8a3
Merge branch 'next' of https://github.com/Geode-solutions/OpenGeodeWe…
SpliiT Jul 24, 2026
126ba11
fix comments front
SpliiT Jul 24, 2026
f5145b3
Apply prepare changes
SpliiT Jul 24, 2026
7edea04
clear oxlint
SpliiT Jul 24, 2026
60edab4
Merge branch 'feat/HorizonStack' of https://github.com/Geode-solution…
SpliiT Jul 24, 2026
df2242e
Apply prepare changes
SpliiT Jul 24, 2026
66c7cf3
trigger
SpliiT Jul 27, 2026
ed3a7a8
create new finles in order to fix oxlint
SpliiT Jul 27, 2026
ad3e399
Apply prepare changes
SpliiT Jul 27, 2026
7201164
Merge branch 'next' of https://github.com/Geode-solutions/OpenGeodeWe…
SpliiT Jul 28, 2026
ff7eb08
fix ox and tests
SpliiT Jul 29, 2026
8e74570
Apply prepare changes
SpliiT Jul 29, 2026
24bb8a6
Merge branch 'next' of https://github.com/Geode-solutions/OpenGeodeWe…
SpliiT Jul 30, 2026
9240320
Merge branch 'feat/HorizonStack' of https://github.com/Geode-solution…
SpliiT Jul 30, 2026
9f9fcb7
correct test
SpliiT Jul 30, 2026
f88c45d
go release
SpliiT Jul 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 58 additions & 20 deletions app/components/Viewer/ObjectTree/Base/TreeRow.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
<script setup>
import { useDataStore } from "@ogw_front/stores/data";

const dataStore = useDataStore();

const { item, itemProps, selection, isSelected, getIndeterminate } = defineProps({
item: { type: Object, required: true },
itemProps: { type: Object, required: true },
Expand All @@ -7,13 +11,34 @@ const { item, itemProps, selection, isSelected, getIndeterminate } = defineProps
getIndeterminate: { type: Function, required: true },
});

defineEmits(["toggle-open", "toggle-select", "hover-eye-enter", "hover-eye-leave"]);
const emit = defineEmits(["toggle-open", "toggle-select", "hover-eye-enter", "hover-eye-leave"]);

const INDENT_STEP = 10;

function triggerHorizonStackModal(rawItem) {
globalThis.dispatchEvent(new CustomEvent("open-horizon-stack-modal", { detail: rawItem }));
}
const isHorizonStack = computed(() => item.raw.geode_object_type === "HorizonStack3D");
const isViewable = computed(() => dataStore.isItemViewable(item.raw));
const showEyeButton = computed(
() => !isHorizonStack.value && item.raw.title !== "HorizonStack3D" && isViewable.value,
);

function handleRowClick(event) {
if (isHorizonStack.value) {
if (!item.isLeaf) {
return;
}

event.stopPropagation();
event.preventDefault();
triggerHorizonStackModal(item.raw);
}
}
</script>

<template>
<div class="tree-row-content d-flex align-center px-2 ps-2 w-100">
<div class="tree-row-content d-flex align-center px-2 ps-2 w-100" @click="handleRowClick">
<div
v-if="item.depth > 0"
class="flex-shrink-0"
Expand All @@ -30,24 +55,37 @@ const INDENT_STEP = 10;
/>
<div v-else class="icon-placeholder" />

<v-btn
v-if="selection.selectable"
:icon="
getIndeterminate(item.raw)
? 'mdi-eye-minus-outline'
: isSelected(item.raw)
? 'mdi-eye'
: 'mdi-eye-off-outline'
"
variant="text"
density="compact"
color="black"
class="flex-shrink-0"
@click.stop="$emit('toggle-select', item.raw)"
@mousedown.stop
@mouseenter="$emit('hover-eye-enter', item.raw)"
@mouseleave="$emit('hover-eye-leave', item.raw)"
/>
<template v-if="selection.selectable">
<v-btn
v-if="isHorizonStack && item.isLeaf"
icon="mdi-layers-triple"
variant="text"
density="compact"
color="black"
class="flex-shrink-0"
style="z-index: 4"
@click.stop="triggerHorizonStackModal(item.raw)"
@mousedown.stop
/>
<v-btn
v-else-if="showEyeButton"
:icon="
getIndeterminate(item.raw)
? 'mdi-eye-minus-outline'
: isSelected(item.raw)
? 'mdi-eye'
: 'mdi-eye-off-outline'
"
variant="text"
density="compact"
color="black"
class="flex-shrink-0"
@click.stop="$emit('toggle-select', item.raw)"
@mousedown.stop
@mouseenter="$emit('hover-eye-enter', item.raw)"
@mouseleave="$emit('hover-eye-leave', item.raw)"
/>
</template>
</div>

<div class="tree-title flex-grow-1 overflow-hidden d-flex align-center ms-1 pt-1">
Expand Down
82 changes: 42 additions & 40 deletions app/components/Viewer/ObjectTree/Views/GlobalObjects.vue
Original file line number Diff line number Diff line change
Expand Up @@ -179,46 +179,48 @@ function expandAll() {
</template>

<template #append="{ item }">
<v-btn
v-if="item.viewer_type"
icon="mdi-target"
size="medium"
variant="text"
v-tooltip="'Focus camera on object'"
@click.stop="hybridViewerStore.focusCameraOnObject(item.id)"
/>
<v-btn
v-if="isModel(item)"
icon="mdi-magnify-expand"
size="medium"
class="ml-2"
variant="text"
v-tooltip="'Model\'s mesh components'"
@click.stop="
treeviewStore.displayAdditionalTree(
item.id,
item.title,
item.geode_object_type,
'model_components',
)
"
/>
<v-btn
v-if="isModel(item) && hasCollectionsMap[item.id]"
icon="mdi-format-list-group"
size="medium"
class="ml-2"
variant="text"
v-tooltip="'Model\'s collections'"
@click.stop="
treeviewStore.displayAdditionalTree(
item.id,
item.title,
item.geode_object_type,
'model_collections',
)
"
/>
<template v-if="item.geode_object_type !== 'HorizonStack3D'">
<v-btn
v-if="item.viewer_type"
icon="mdi-target"
size="medium"
variant="text"
v-tooltip="'Focus camera on object'"
@click.stop="hybridViewerStore.focusCameraOnObject(item.id)"
/>
<v-btn
v-if="isModel(item)"
icon="mdi-magnify-expand"
size="medium"
class="ml-2"
variant="text"
v-tooltip="'Model\'s mesh components'"
@click.stop="
treeviewStore.displayAdditionalTree(
item.id,
item.title,
item.geode_object_type,
'model_components',
)
"
/>
<v-btn
v-if="isModel(item) && hasCollectionsMap[item.id]"
icon="mdi-format-list-group"
size="medium"
class="ml-2"
variant="text"
v-tooltip="'Model\'s collections'"
@click.stop="
treeviewStore.displayAdditionalTree(
item.id,
item.title,
item.geode_object_type,
'model_collections',
)
"
/>
</template>
</template>
</CommonTreeView>
</div>
Expand Down
7 changes: 7 additions & 0 deletions app/composables/hover_highlight.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { useDataStore } from "@ogw_front/stores/data";
import { useViewerStore } from "@ogw_front/stores/viewer";
import vtk_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json";

const HOVER_DELAY = 200;

export function useHoverhighlight() {
const viewerStore = useViewerStore();
const dataStore = useDataStore();
let timer = undefined;
let currentId = undefined;
let currentType = undefined;
Expand All @@ -20,6 +22,11 @@ export function useHoverhighlight() {
currentId = id;
currentType = type;

const value = await dataStore.item(id);
if (value && !dataStore.isItemViewable(value)) {
return;
}

let block_ids = [];
if (typeof block_ids_provider === "function") {
block_ids = await block_ids_provider();
Expand Down
18 changes: 16 additions & 2 deletions app/composables/use_overlapping_picker.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,22 @@ export function useOverlappingPicker() {
}

async function get_viewer_id({ x, y, containerWidth, containerHeight, containerRect }) {
const activeIds = new Set(dataItems.value.map((item) => item.id));
const ids = Object.keys(dataStyleStore.styles).filter((styleId) => activeIds.has(styleId));
const activeIds = new Set(dataItems.value.map((i) => i.id));
const visibleStyleIds = Object.keys(dataStyleStore.styles).filter(
(styleId) => activeIds.has(styleId) && dataStyleStore.objectVisibility(styleId),
);

const viewableChecks = await Promise.all(
visibleStyleIds.map(async (styleId) => {
try {
const dataItem = await dataStore.item(styleId);
return dataStore.isItemViewable(dataItem) ? styleId : undefined;
} catch {
return undefined;
}
}),
);
const ids = viewableChecks.filter(Boolean);

const result = { id: undefined, viewer_id: undefined };
let pickedResponse = undefined;
Expand Down
20 changes: 20 additions & 0 deletions app/stores/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,23 @@ import { useInfraStore } from "@ogw_front/stores/infra";
// oxlint-disable-next-line max-lines-per-function, max-statements
export const useAppStore = defineStore("app", () => {
const stores = [];
const globalComponents = ref(new Map());

function registerGlobalComponent(extensionId, componentId, component) {
if (!globalComponents.value.has(extensionId)) {
globalComponents.value.set(extensionId, new Map());
}
globalComponents.value.get(extensionId).set(componentId, component);
console.log(
`[AppStore] Registered global component ${componentId} for extension ${extensionId}`,
);
}

function unregisterGlobalComponent(extensionId, componentId) {
if (globalComponents.value.has(extensionId)) {
globalComponents.value.get(extensionId).delete(componentId);
}
}

function registerStore(store) {
const isAlreadyRegistered = stores.some((registeredStore) => registeredStore.$id === store.$id);
Expand Down Expand Up @@ -285,5 +302,8 @@ export const useAppStore = defineStore("app", () => {
createProjectFolder,
start_request,
stop_request,
globalComponents,
registerGlobalComponent,
unregisterGlobalComponent,
};
});
19 changes: 18 additions & 1 deletion app/stores/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,19 @@ import { useViewerStore } from "@ogw_front/stores/viewer";

const viewer_generic_schemas = viewer_schemas.opengeodeweb_viewer.generic;

function isItemViewable(item) {
if (!item || typeof item !== "object") {
return false;
}
if (item.is_viewable !== undefined) {
return Boolean(item.is_viewable);
}
if (item.binary_light_viewable !== undefined) {
return item.binary_light_viewable !== "not_viewable";
}
return true;
}

// oxlint-disable-next-line max-lines-per-function, max-statements
export const useDataStore = defineStore("data", () => {
const viewerStore = useViewerStore();
Expand Down Expand Up @@ -93,8 +106,11 @@ export const useDataStore = defineStore("data", () => {
geode_object_type: new_item.geode_object_type,
visible: true,
created_at: new Date().toISOString(),
binary_light_viewable: new_item.binary_light_viewable,
is_viewable: new_item.is_viewable,
};
if (new_item.binary_light_viewable !== undefined && new_item.binary_light_viewable !== null) {
itemData.binary_light_viewable = new_item.binary_light_viewable;
}
return data_db.put(itemData);
}

Expand Down Expand Up @@ -213,6 +229,7 @@ export const useDataStore = defineStore("data", () => {
item,
allItems,
refItem,
isItemViewable,
meshComponentType,
registerObject,
deregisterObject,
Expand Down
11 changes: 11 additions & 0 deletions app/stores/data_style.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ export const useDataStyleStore = defineStore("dataStyle", () => {

async function setVisibility(id, visibility) {
const item = await dataStore.item(id);
if (!dataStore.isItemViewable(item)) {
return dataStyleState.mutateStyle(id, { visibility });
}

const { viewer_type } = item;

if (viewer_type === "mesh") {
Expand All @@ -34,6 +38,10 @@ export const useDataStyleStore = defineStore("dataStyle", () => {

async function applyDefaultStyle(id) {
const item = await dataStore.item(id);
if (!dataStore.isItemViewable(item)) {
throw new Error(`applyDefaultStyle called for non-viewable item: ${id}`);
}

const { viewer_type } = item;

if (viewer_type === "mesh") {
Expand Down Expand Up @@ -81,6 +89,9 @@ export const useDataStyleStore = defineStore("dataStyle", () => {
const ids = Object.keys(dataStyleState.styles.value);
const promises = ids.map(async (id) => {
const meta = await dataStore.item(id);
if (!dataStore.isItemViewable(meta)) {
return;
}
const viewerType = meta.viewer_type;
if (viewerType === "mesh") {
return meshStyleStore.applyMeshStyle(id);
Expand Down
10 changes: 5 additions & 5 deletions app/stores/hybrid_viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ import {
performSyncRemoteCamera,
} from "@ogw_internal/stores/hybrid_viewer_camera";
import {
computeAverageBrightness,
createClearHoverData,
createHoverHighlight,
} from "@ogw_internal/stores/hybrid_viewer_highlight";
import {
performAddItem,
performClear,
performClearHoverHighlight,
Expand All @@ -22,10 +25,7 @@ import {
performSetVisibility,
performSetZScaling,
} from "@ogw_internal/stores/hybrid_viewer";
import {
createClearHoverData,
createHoverHighlight,
} from "@ogw_internal/stores/hybrid_viewer_highlight";
import { computeAverageBrightness } from "@ogw_internal/stores/hybrid_viewer_brightness";
import { newInstance as vtkActor } from "@kitware/vtk.js/Rendering/Core/Actor";
import { newInstance as vtkGenericRenderWindow } from "@kitware/vtk.js/Rendering/Misc/GenericRenderWindow";
import { newInstance as vtkMapper } from "@kitware/vtk.js/Rendering/Core/Mapper";
Expand Down
9 changes: 7 additions & 2 deletions app/utils/import_workflow.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,18 @@ async function importItem(item) {
treeviewStore.addItem(item.geode_object_type, item.name, item.id, item.viewer_type);
const addDataStyleTask = dataStyleStore.addDataStyle(item.id, item.geode_object_type);
const addViewerTask = addDataTask.then(() => {
if (item.nb_vertices === 0) {
if (!dataStore.isItemViewable(item)) {
return;
}
return hybridViewerStore.addItem(item.id);
});
const applyStyleTask = Promise.all([registerTask, addDataComponentsTask, addDataStyleTask]).then(
() => dataStyleStore.applyDefaultStyle(item.id),
() => {
if (dataStore.isItemViewable(item)) {
return dataStyleStore.applyDefaultStyle(item.id);
}
return undefined;
},
);
await Promise.all([
registerTask,
Expand Down
Loading
Loading