Skip to content

Commit

Permalink
Merge pull request #488 from bnmajor/shift-opacity
Browse files Browse the repository at this point in the history
feat(vtkPiecewiseWidget): Support vertical movement of points
  • Loading branch information
floryst authored Nov 14, 2023
2 parents 78dd81d + a998a42 commit 411e5a8
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 14 deletions.
8 changes: 7 additions & 1 deletion src/components/VolumeRendering.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
watch,
watchEffect,
} from 'vue';
import { onKeyDown, onKeyUp } from '@vueuse/core';
import { PresetNameList } from '@/src/vtk/ColorMaps';
import vtkPiecewiseWidget from '@/src/vtk/PiecewiseWidget';
import type { vtkSubscription } from '@kitware/vtk.js/interfaces';
Expand Down Expand Up @@ -119,6 +120,7 @@ export default defineComponent({
{
mode,
shift: pwfWidget.getOpacityPointShift(),
shiftAlpha: pwfWidget.getOpacityValueShift(),
}
);
}
Expand Down Expand Up @@ -227,9 +229,10 @@ export default defineComponent({
const points = getShiftedOpacityFromPreset(
opFunc.preset,
opFunc.mappingRange,
0,
0
);
pwfWidget.setOpacityPoints(points, opFunc.shift);
pwfWidget.setOpacityPoints(points, opFunc.shift, opFunc.shiftAlpha);
}
},
{ immediate: true }
Expand Down Expand Up @@ -279,6 +282,9 @@ export default defineComponent({
const rangeShift = ref(0);
const rangeWidth = ref(0);
onKeyDown('Control', () => pwfWidget.setShiftOpacityValues(true));
onKeyUp('Control', () => pwfWidget.setShiftOpacityValues(false));
// reset case
watch(
[selectedPreset, currentImageID],
Expand Down
3 changes: 2 additions & 1 deletion src/components/VtkThreeView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,8 @@ function useColoringEffect(
const opacityPoints = getShiftedOpacityFromPreset(
opacityFunc.preset,
opacityFunc.mappingRange,
opacityFunc.shift
opacityFunc.shift,
opacityFunc.shiftAlpha
);
if (opacityPoints) {
pwf.setPoints(opacityPoints);
Expand Down
3 changes: 2 additions & 1 deletion src/components/VtkTwoView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -823,7 +823,8 @@ export default defineComponent({
const opacityPoints = getShiftedOpacityFromPreset(
opFunc.preset,
opFunc.mappingRange,
opFunc.shift
opFunc.shift,
opFunc.shiftAlpha
);
if (opacityPoints) {
pwf.setPoints(opacityPoints);
Expand Down
1 change: 1 addition & 0 deletions src/io/state-file/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ const OpacityPoints = z.object({
mode: z.literal(vtkPiecewiseFunctionProxy.Mode.Points),
preset: z.string(),
shift: z.number(),
shiftAlpha: z.number(),
mappingRange: z.tuple([z.number(), z.number()]),
}) satisfies z.ZodType<OpacityPoints>;

Expand Down
1 change: 1 addition & 0 deletions src/types/views.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface OpacityPoints {
// base preset that has the opacity points
preset: string;
shift: number;
shiftAlpha: number;
mappingRange: [number, number];
}

Expand Down
12 changes: 10 additions & 2 deletions src/utils/vtk-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ export function getCSSCoordinatesFromEvent(eventData: any) {
export function getShiftedOpacityFromPreset(
presetName: string,
effectiveRange: [number, number],
shift: number
shift: number,
shiftAlpha: number
) {
const preset = vtkColorMaps.getPresetByName(presetName);
if (preset.OpacityPoints) {
Expand All @@ -128,7 +129,13 @@ export function getShiftedOpacityFromPreset(

const [xmin, xmax] = effectiveRange;
const width = xmax - xmin;
return points.map(([x, y]) => [(x - xmin) / width + shift, y]);
return points.map(([x, y]) => {
// Non-zero values should be affected by shift
// but preset values of zero should not
const shifted = y && y - shiftAlpha;
const yVal = Math.max(Math.min(shifted, 1), 0);
return [(x - xmin) / width + shift, yVal];
});
}
return null;
}
Expand All @@ -146,6 +153,7 @@ export function getOpacityFunctionFromPreset(
mode: vtkPiecewiseFunctionProxy.Mode.Points,
preset: presetName,
shift: 0,
shiftAlpha: 0,
};
}
return {
Expand Down
45 changes: 36 additions & 9 deletions src/vtk/PiecewiseWidget/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ export const ADJUST_POSITION_CURSOR = '-webkit-grab';
* Assumes normalized x/y coordinate points.
*/

export function samplePiecewiseLinear(points, shift = 0, samples = 256) {
export function samplePiecewiseLinear(
points,
shift = 0,
shiftAlpha = 0,
samples = 256
) {
const filledPoints = [...points];
// set endpoints to drop to 0
filledPoints.push([points[0][0], 0]);
Expand Down Expand Up @@ -47,7 +52,11 @@ export function samplePiecewiseLinear(points, shift = 0, samples = 256) {
} else if (slope === -Infinity) {
sampledPoints.push(0);
} else {
sampledPoints.push(slope * (sx - p1[0]) + p1[1]);
// Non-zero values should be affected by shift
// but original values of zero should not
let value = slope * (sx - p1[0]) + p1[1];
value = value && value - shiftAlpha;
sampledPoints.push(value);
}
}
}
Expand All @@ -66,6 +75,7 @@ function vtkPiecewiseWidget(publicAPI, model) {
model.pwMode = Mode.Gaussians;
model.opacityPoints = [];
model.opacityPointShift = 0;
model.opacityValueShift = 0;

publicAPI.setGaussiansMode = () => {
model.pwMode = Mode.Gaussians;
Expand All @@ -82,8 +92,13 @@ function vtkPiecewiseWidget(publicAPI, model) {
publicAPI.getMode = () => model.pwMode;

publicAPI.shiftPosition = (coords, meta) => {
model.opacityPointShift =
meta.originalOpacityPointShift + coords[0] - meta.originalXY[0];
if (model.shiftOpacityValues) {
model.opacityValueShift =
meta.originalOpacityValueShift + coords[1] - meta.originalXY[1];
} else {
model.opacityPointShift =
meta.originalOpacityPointShift + coords[0] - meta.originalXY[0];
}
return true;
};

Expand Down Expand Up @@ -122,6 +137,7 @@ function vtkPiecewiseWidget(publicAPI, model) {
model.dragAction = {
originalXY: mouseCoords,
originalOpacityPointShift: model.opacityPointShift,
originalOpacityValueShift: model.opacityValueShift,
};

return true;
Expand All @@ -140,7 +156,8 @@ function vtkPiecewiseWidget(publicAPI, model) {
if (publicAPI.shiftPosition(normCoords, model.dragAction)) {
model.opacities = samplePiecewiseLinear(
model.opacityPoints,
model.opacityPointShift
model.opacityPointShift,
model.opacityValueShift
);
publicAPI.invokeOpacityChange(publicAPI, true);
}
Expand All @@ -149,22 +166,27 @@ function vtkPiecewiseWidget(publicAPI, model) {
return true;
};

publicAPI.setOpacityPoints = (points, shift = 0) => {
publicAPI.setOpacityPoints = (points, shift = 0, shiftAlpha = 0) => {
if (publicAPI.isModePoints()) {
// deep copy
model.opacityPoints = points.map((p) => [p[0], p[1]]);
model.opacityPointShift = shift;
model.opacityValueShift = shiftAlpha;

model.opacities = samplePiecewiseLinear(
model.opacityPoints,
model.opacityPointShift
model.opacityPointShift,
model.opacityValueShift
);
publicAPI.modified();
}
};

publicAPI.getEffectiveOpacityPoints = () =>
model.opacityPoints.map((p) => [p[0] + model.opacityPointShift, p[1]]);
model.opacityPoints.map((p) => [
p[0] + model.opacityPointShift,
p[1] + model.opacityValueShift,
]);

publicAPI.render = () => {
if (publicAPI.isModePoints()) {
Expand All @@ -188,7 +210,12 @@ export function extend(publicAPI, model, initialValues = {}) {

vtkPiecewiseGaussianWidget.extend(publicAPI, model, initialValues);

macro.setGet(publicAPI, model, ['opacityPoints', 'opacityPointShift']);
macro.setGet(publicAPI, model, [
'opacityPoints',
'opacityPointShift',
'opacityValueShift',
'shiftOpacityValues',
]);

// Object specific methods
vtkPiecewiseWidget(publicAPI, model);
Expand Down

0 comments on commit 411e5a8

Please sign in to comment.