Skip to content

Commit

Permalink
Merge pull request #379 from floryst/refactor-widgets
Browse files Browse the repository at this point in the history
Refactor widgets
  • Loading branch information
floryst authored Oct 30, 2020
2 parents 366bd6c + 22509c8 commit ba8fcb0
Show file tree
Hide file tree
Showing 13 changed files with 105 additions and 619 deletions.
185 changes: 4 additions & 181 deletions src/vtk/AngleWidget/behavior.js
Original file line number Diff line number Diff line change
@@ -1,185 +1,8 @@
import macro from 'vtk.js/Sources/macro';
import superWidgetBehavior from 'vtk.js/Sources/Widgets/Widgets3D/AngleWidget/behavior';

const MAX_POINTS = 3;
import widgetManipulatorWatcher from 'paraview-glance/src/vtk/widgetManipulatorWatcher';

export default function widgetBehavior(publicAPI, model) {
model.classHierarchy.push('vtkAngleWidgetProp');
let isDragging = null;
let cameraSub = null;

// --------------------------------------------------------------------------
// Display 2D
// --------------------------------------------------------------------------

publicAPI.setDisplayCallback = (callback) =>
model.representations[0].setDisplayCallback(callback);

// --------------------------------------------------------------------------
// Interactor events
// --------------------------------------------------------------------------

function ignoreKey(e) {
return e.altKey || e.controlKey || e.shiftKey;
}

// --------------------------------------------------------------------------
// Left press: Select handle to drag
// --------------------------------------------------------------------------

publicAPI.handleLeftButtonPress = (e) => {
if (
!model.activeState ||
!model.activeState.getActive() ||
!model.pickable ||
ignoreKey(e)
) {
return macro.VOID;
}

if (
model.activeState === model.widgetState.getMoveHandle() &&
model.widgetState.getHandleList().length < MAX_POINTS
) {
// Commit handle to location
const moveHandle = model.widgetState.getMoveHandle();
const newHandle = model.widgetState.addHandle();
newHandle.setOrigin(...moveHandle.getOrigin());
newHandle.setColor(moveHandle.getColor());
newHandle.setScale1(moveHandle.getScale1());
} else {
isDragging = true;
model.openGLRenderWindow.setCursor('grabbing');
model.interactor.requestAnimation(publicAPI);
}

publicAPI.invokeStartInteractionEvent();
return macro.EVENT_ABORT;
};

// --------------------------------------------------------------------------
// Mouse move: Drag selected handle / Handle follow the mouse
// --------------------------------------------------------------------------

publicAPI.handleMouseMove = (callData) => {
if (
model.hasFocus &&
model.widgetState.getHandleList().length === MAX_POINTS
) {
model.widgetManager.releaseFocus();
return macro.VOID;
}

if (
model.pickable &&
model.manipulator &&
model.activeState &&
model.activeState.getActive() &&
!ignoreKey(callData)
) {
const worldCoords = model.manipulator.handleEvent(
callData,
model.openGLRenderWindow
);
if (
worldCoords.length &&
(model.activeState === model.widgetState.getMoveHandle() || isDragging)
) {
model.activeState.setOrigin(worldCoords);
publicAPI.invokeInteractionEvent();
return macro.EVENT_ABORT;
}
}

return macro.VOID;
};

// --------------------------------------------------------------------------
// Left release: Finish drag / Create new handle
// --------------------------------------------------------------------------

publicAPI.handleLeftButtonRelease = () => {
if (isDragging && model.pickable) {
model.openGLRenderWindow.setCursor('pointer');
model.widgetState.deactivate();
model.interactor.cancelAnimation(publicAPI);
publicAPI.invokeEndInteractionEvent();
} else if (model.activeState !== model.widgetState.getMoveHandle()) {
model.widgetState.deactivate();
}

if (
(model.hasFocus && !model.activeState) ||
(model.activeState && !model.activeState.getActive())
) {
publicAPI.invokeEndInteractionEvent();
}

isDragging = false;
};

// --------------------------------------------------------------------------
// Focus API - modeHandle follow mouse when widget has focus
// --------------------------------------------------------------------------

publicAPI.grabFocus = () => {
if (
!model.hasFocus &&
model.widgetState.getHandleList().length < MAX_POINTS
) {
model.activeState = model.widgetState.getMoveHandle();
model.activeState.activate();
model.activeState.setVisible(true);
model.interactor.requestAnimation(publicAPI);
publicAPI.invokeStartInteractionEvent();
}
model.hasFocus = true;
};

// --------------------------------------------------------------------------

publicAPI.loseFocus = () => {
if (model.hasFocus) {
model.interactor.cancelAnimation(publicAPI);
publicAPI.invokeEndInteractionEvent();
}
model.widgetState.deactivate();
model.widgetState.getMoveHandle().deactivate();
model.widgetState.getMoveHandle().setVisible(false);
model.activeState = null;
model.hasFocus = false;
model.widgetManager.enablePicking();
model.interactor.render();
};

// --------------------------------------------------------------------------

publicAPI.delete = macro.chain(publicAPI.delete, () => {
if (cameraSub) {
cameraSub.unsubscribe();
}
});

// --------------------------------------------------------------------------
// init
// --------------------------------------------------------------------------

const setHandleScaleFromCamera = () => {
if (model.camera) {
let scale;
if (model.camera.getParallelProjection()) {
scale = model.camera.getParallelScale() / 1.25;
} else {
scale = model.camera.getDistance() / 6;
}

publicAPI.setHandleScale(scale);
}
};

// listen to camera so we can scale the handles to the screen
cameraSub = model.camera.onModified(setHandleScaleFromCamera);
// since forwarded/linked set/get methods aren't created until
// after this behavior function finishes, this is a hack to invoke
// initial handle scale.
setTimeout(setHandleScaleFromCamera, 0);
superWidgetBehavior(publicAPI, model);
widgetManipulatorWatcher(publicAPI, model);
}
73 changes: 21 additions & 52 deletions src/vtk/AngleWidget/index.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import macro from 'vtk.js/Sources/macro';
import vtkAbstractWidgetFactory from 'vtk.js/Sources/Widgets/Core/AbstractWidgetFactory';
import vtkPlanePointManipulator from 'vtk.js/Sources/Widgets/Manipulators/PlaneManipulator';
import * as vtkMath from 'vtk.js/Sources/Common/Core/Math';
import vtkSphereHandleRepresentation from 'vtk.js/Sources/Widgets/Representations/SphereHandleRepresentation';
import vtkAngleWidget from 'vtk.js/Sources/Widgets/Widgets3D/AngleWidget';

import widgetBehavior from 'paraview-glance/src/vtk/AngleWidget/behavior';
import stateGenerator from 'paraview-glance/src/vtk/AngleWidget/state';

import vtkScaledSphereHandleRepresentation from 'paraview-glance/src/vtk/ScaledSphereHandleRepresentation';
import vtkSVGCircleHandleRepresentation from 'paraview-glance/src/vtk/SVGCircleHandleRepresentation';
import vtkSVGLineRepresentation from 'paraview-glance/src/vtk/SVGLineRepresentation';
import vtkSVGLabelRepresentation from 'paraview-glance/src/vtk/SVGLabelRepresentation';
Expand All @@ -17,24 +15,20 @@ import { ViewTypes } from 'vtk.js/Sources/Widgets/Core/WidgetManager/Constants';
// Factory
// ----------------------------------------------------------------------------

function vtkAngleWidget(publicAPI, model) {
model.classHierarchy.push('vtkAngleWidget');
function vtkAngle2DWidget(publicAPI, model) {
model.classHierarchy.push('vtkAngle2DWidget');

// --- Widget Requirement ---------------------------------------------------

model.methodsToLink = [
'activeScaleFactor',
'activeColor',
'useActiveColor',
'glyphResolution',
'defaultScale',
...(model.methodsToLink ?? []),
'circleProps',
'lineProps',
'textProps',
'text',
'textStateIndex',
'handleScale',
];

model.behavior = widgetBehavior;
model.widgetState = stateGenerator();

Expand All @@ -46,10 +40,19 @@ function vtkAngleWidget(publicAPI, model) {
case ViewTypes.VOLUME:
default:
return [
{ builder: vtkScaledSphereHandleRepresentation, labels: ['handles'] },
{
builder: vtkScaledSphereHandleRepresentation,
builder: vtkSphereHandleRepresentation,
labels: ['handles'],
initialValues: {
scaleInPixels: true,
},
},
{
builder: vtkSphereHandleRepresentation,
labels: ['moveHandle'],
initialValues: {
scaleInPixels: true,
},
},
{
builder: vtkSVGCircleHandleRepresentation,
Expand All @@ -66,60 +69,26 @@ function vtkAngleWidget(publicAPI, model) {
];
}
};

// --- Public methods -------------------------------------------------------

// Returns angle in radians
publicAPI.getAngle = () => {
const handles = model.widgetState.getHandleList();
if (handles.length !== 3) {
return 0;
}

const vec1 = [0, 0, 0];
const vec2 = [0, 0, 0];
vtkMath.subtract(handles[0].getOrigin(), handles[1].getOrigin(), vec1);
vtkMath.subtract(handles[2].getOrigin(), handles[1].getOrigin(), vec2);
return vtkMath.angleBetweenVectors(vec1, vec2);
};

// --------------------------------------------------------------------------
// initialization
// --------------------------------------------------------------------------

model.widgetState.onBoundsChange((bounds) => {
const center = [
(bounds[0] + bounds[1]) * 0.5,
(bounds[2] + bounds[3]) * 0.5,
(bounds[4] + bounds[5]) * 0.5,
];
model.widgetState.getMoveHandle().setOrigin(center);
});

// Default manipulator
model.manipulator = vtkPlanePointManipulator.newInstance();
}

// ----------------------------------------------------------------------------

const DEFAULT_VALUES = {
// manipulator: null,
};
const DEFAULT_VALUES = {};

// ----------------------------------------------------------------------------

export function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);

vtkAbstractWidgetFactory.extend(publicAPI, model, initialValues);
vtkAngleWidget.extend(publicAPI, model, initialValues);
macro.setGet(publicAPI, model, ['manipulator']);

vtkAngleWidget(publicAPI, model);
vtkAngle2DWidget(publicAPI, model);
}

// ----------------------------------------------------------------------------

export const newInstance = macro.newInstance(extend, 'vtkAngleWidget');
export const newInstance = macro.newInstance(extend, 'vtkAngle2DWidget');

// ----------------------------------------------------------------------------

Expand Down
4 changes: 2 additions & 2 deletions src/vtk/AngleWidget/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default function generateState() {
mixins: ['origin', 'color', 'scale1', 'visible'],
name: 'moveHandle',
initialValues: {
scale1: 0.1,
scale1: 50,
origin: [-1, -1, -1],
visible: false,
},
Expand All @@ -18,7 +18,7 @@ export default function generateState() {
mixins: ['origin', 'color', 'scale1'],
name: 'handle',
initialValues: {
scale1: 0.1,
scale1: 50,
origin: [-1, -1, -1],
},
})
Expand Down
31 changes: 1 addition & 30 deletions src/vtk/CropWidget/behavior.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,5 @@
import macro from 'vtk.js/Sources/macro';

export default function widgetBehavior(publicAPI, model) {
model.classHierarchy.push('vtkCropWidgetProp');
let cameraSub = null;

// --------------------------------------------------------------------------

publicAPI.delete = macro.chain(publicAPI.delete, () => {
if (cameraSub) {
cameraSub.unsubscribe();
}
});

// --------------------------------------------------------------------------
// init
Expand All @@ -30,23 +19,5 @@ export default function widgetBehavior(publicAPI, model) {
model.widgetState
.getAllNestedStates()
.filter((state) => !!state.setScale1)
.forEach((state) => state.setScale1(0.075));

const setHandleScaleFromCamera = () => {
let scale;
if (model.camera.getParallelProjection()) {
scale = model.camera.getParallelScale() / 1.25;
} else {
scale = model.camera.getDistance() / 6;
}

publicAPI.setHandleScale(scale);
};

// listen to camera so we can scale the handles to the screen
cameraSub = model.camera.onModified(setHandleScaleFromCamera);
// since forwarded/linked set/get methods aren't created until
// after this behavior function finishes, this is a hack to invoke
// initial handle scale.
setTimeout(setHandleScaleFromCamera, 0);
.forEach((state) => state.setScale1(20));
}
Loading

0 comments on commit ba8fcb0

Please sign in to comment.