Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions app/components/HybridRenderingView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ function debounce(func, wait) {

async function handleClick(event) {
const { offsetX, offsetY, clientX, clientY } = event;
if (hybridViewerStore.is_ruler_active) {
const rect = container.value.$el.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = elementHeight.value - (event.clientY - rect.top);
await hybridViewerStore.handleRulerClick(x, y);
return;
}

if (viewerStore.picking_mode) {
const rect = container.value.$el.getBoundingClientRect();
const x = event.clientX - rect.left;
Expand Down
144 changes: 144 additions & 0 deletions app/components/Ruler.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<script setup>
import ToolPanel from "@ogw_front/components/ToolPanel";
import { useHybridViewerStore } from "@ogw_front/stores/hybrid_viewer";

const show = defineModel("show", { type: Boolean, default: false });
const hybridViewerStore = useHybridViewerStore();

const localPoint1 = ref([0, 0, 0]);
const localPoint2 = ref([0, 0, 0]);

watch(
() => hybridViewerStore.ruler_point1,
(point) => {
if (point) {
localPoint1.value = [...point];
}
},
);

watch(
() => hybridViewerStore.ruler_point2,
(point) => {
if (point) {
localPoint2.value = [...point];
}
},
);

watch(show, (visible) => {
if (visible) {
hybridViewerStore.is_ruler_active = true;
} else if (hybridViewerStore.is_ruler_active) {
hybridViewerStore.deactivateRuler();
}
});

watch(
() => hybridViewerStore.is_ruler_active,
(active) => {
if (!active) {
show.value = false;
}
},
);

async function applyManualCoords() {
hybridViewerStore.ruler_point1 = [...localPoint1.value];
hybridViewerStore.ruler_point2 = [...localPoint2.value];
await hybridViewerStore.applyRuler();
}
</script>

<template>
<ToolPanel
v-model="show"
title="Ruler"
:width="300"
close-label="Stop ruler"
:click-outside="false"
>
<v-card-text class="pa-3">
<v-switch
v-model="hybridViewerStore.ruler_snap"
data-testid="rulerSnapToggle"
label="Snap to vertex"
color="primary"
density="compact"
hide-details
class="mb-4"
/>

<v-divider class="mb-3" />

<div class="text-caption font-weight-bold mb-2 text-medium-emphasis">Point 1</div>
<v-row dense class="mb-1" data-testid="rulerPointCard">
<v-col v-for="(axis, index) in ['X', 'Y', 'Z']" :key="axis">
<v-text-field
v-model.number="localPoint1[index]"
data-testid="rulerPointCoordInput"
:label="axis"
type="number"
density="compact"
variant="outlined"
hide-details
/>
</v-col>
</v-row>

<div class="text-caption font-weight-bold mb-2 mt-3 text-medium-emphasis">Point 2</div>
<v-row dense class="mb-1" data-testid="rulerPointCard">
<v-col v-for="(axis, index) in ['X', 'Y', 'Z']" :key="axis">
<v-text-field
v-model.number="localPoint2[index]"
data-testid="rulerPointCoordInput"
:label="axis"
type="number"
density="compact"
variant="outlined"
hide-details
/>
</v-col>
</v-row>

<v-btn
data-testid="rulerApplyButton"
variant="tonal"
size="small"
color="primary"
block
class="mt-3 text-caption text-none"
@click="applyManualCoords"
>
Apply
</v-btn>

<v-divider class="my-3" />

<div v-if="hybridViewerStore.ruler_distance !== undefined" class="text-center">
<div class="text-caption text-medium-emphasis mb-1">Distance</div>
<div data-testid="rulerDistance" class="text-h6 font-weight-bold">
{{ hybridViewerStore.ruler_distance.toFixed(4) }}
</div>
</div>
<div v-else class="text-caption text-medium-emphasis text-center">
Click in the scene to set both points
</div>
</v-card-text>

<template #actions>
<v-card-actions class="justify-center pb-3 pt-0">
<v-btn
data-testid="rulerClearButton"
variant="text"
size="small"
color="error"
class="text-caption text-none"
@click="hybridViewerStore.clearRuler()"
>
Clear
</v-btn>
</v-card-actions>
</template>
</ToolPanel>
</template>
12 changes: 12 additions & 0 deletions app/components/ViewToolbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import CameraBookmarkIcon from "@ogw_front/assets/viewer_svgs/camera-bookmark.sv
import CameraManager from "@ogw_front/components/CameraManager";
import CameraOrientation from "@ogw_front/components/CameraOrientation";
import ClippingPlanes from "@ogw_front/components/ClippingPlanes";
import Ruler from "@ogw_front/components/Ruler";
import Screenshot from "@ogw_front/components/Screenshot";
import ShrinkFilter from "@ogw_front/components/ShrinkFilter";
import ZScaling from "@ogw_front/components/ZScaling";
Expand All @@ -19,6 +20,7 @@ const showCameraOrientation = ref(false);
const showZScaling = ref(false);
const showClippingPlanes = ref(false);
const showShrinkFilter = ref(false);
const showRuler = ref(false);
const grid_scale = ref(false);
const zScale = ref(hybridViewerStore.zScale);

Expand Down Expand Up @@ -173,6 +175,15 @@ const camera_options = computed(() => [
showShrinkFilter.value = !showShrinkFilter.value;
},
},
{
testId: "rulerButton",
tooltip: "Ruler",
icon: "mdi-ruler",
color: showRuler.value ? "primary" : undefined,
action: () => {
showRuler.value = !showRuler.value;
},
},
]);
</script>

Expand Down Expand Up @@ -247,6 +258,7 @@ const camera_options = computed(() => [
/>
<ClippingPlanes v-model:show="showClippingPlanes" />
<ShrinkFilter v-model:show="showShrinkFilter" />
<Ruler v-model:show="showRuler" />
</template>

<style module>
Expand Down
28 changes: 27 additions & 1 deletion app/components/Viewer/Ui.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ function stopHoverHighlight() {
}

onKeyStroke("Escape", () => {
if (hybridViewerStore.is_hover_highlight) {
if (hybridViewerStore.is_ruler_active) {
hybridViewerStore.deactivateRuler();
} else if (hybridViewerStore.is_hover_highlight) {
stopHoverHighlight();
}
});
Expand Down Expand Up @@ -129,6 +131,30 @@ defineExpose({ get_viewer_id });
</v-chip>
</div>
</v-fade-transition>

<v-fade-transition>
<div
v-if="hybridViewerStore.is_ruler_active"
class="picking-message-container d-flex justify-center w-100 pa-4"
>
<v-chip
data-testid="rulerActiveChip"
color="secondary"
elevation="8"
size="large"
variant="flat"
class="pick-pulse"
style="pointer-events: auto"
prepend-icon="mdi-ruler"
@click="hybridViewerStore.clearRuler()"
>
Ruler &mdash; click to set point {{ hybridViewerStore.ruler_awaiting_point }}
&middot; Esc to stop
<v-divider vertical class="mx-2 my-1" opacity="0.3" />
<v-icon icon="mdi-close" size="small" />
</v-chip>
</div>
</v-fade-transition>
</template>

<style scoped>
Expand Down
22 changes: 13 additions & 9 deletions app/stores/hybrid_viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { BACKGROUND_COLOR } from "@ogw_internal/stores/hybrid_viewer/constants";
import { useHybridViewerBrightness } from "@ogw_internal/stores/hybrid_viewer/brightness";
import { useHybridViewerFilters } from "@ogw_internal/stores/hybrid_viewer/filters";
import { useHybridViewerHighlight } from "@ogw_internal/stores/hybrid_viewer/highlight";
import { useHybridViewerRuler } from "@ogw_internal/stores/hybrid_viewer/ruler";
import { useHybridViewerScene } from "@ogw_internal/stores/hybrid_viewer/scene";
import { useHybridViewerViewport } from "@ogw_internal/stores/hybrid_viewer/viewport";
import { newInstance as vtkGenericRenderWindow } from "@kitware/vtk.js/Rendering/Misc/GenericRenderWindow";
Expand All @@ -25,7 +26,17 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => {
const is_picking = ref(false);
let imageStyle = undefined;

watch(is_picking, (value) => {
const brightnessStore = useHybridViewerBrightness();
const sceneStore = useHybridViewerScene();
const filtersStore = useHybridViewerFilters();
const highlightStore = useHybridViewerHighlight();
const rulerStore = useHybridViewerRuler();
const cameraStore = useHybridViewerCamera();
const viewportStore = useHybridViewerViewport();

const is_cursor_crosshair = computed(() => is_picking.value || rulerStore.is_ruler_active.value);

watch(is_cursor_crosshair, (value) => {
if (!genericRenderWindow.value) {
return;
}
Expand All @@ -36,8 +47,6 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => {
}
});

const brightnessStore = useHybridViewerBrightness();

async function initHybridViewer() {
if (status.value !== Status.NOT_CREATED) {
return;
Expand Down Expand Up @@ -94,12 +103,6 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => {
return renderPromise;
}

const sceneStore = useHybridViewerScene();
const filtersStore = useHybridViewerFilters();
const highlightStore = useHybridViewerHighlight();
const cameraStore = useHybridViewerCamera();
const viewportStore = useHybridViewerViewport();

function exportStores() {
const renderer = genericRenderWindow.value.getRenderer();
const camera = renderer.getActiveCamera();
Expand All @@ -123,6 +126,7 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => {
...viewportStore,
...filtersStore,
...highlightStore,
...rulerStore,
...cameraStore,
};
});
Loading
Loading