Skip to content

Commit 7acd46c

Browse files
authored
Merge pull request #464 from floryst/orientation_cube
Add orientation/axes cube
2 parents bf89eaa + b1e09e1 commit 7acd46c

File tree

7 files changed

+535
-0
lines changed

7 files changed

+535
-0
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import vtkImageData from 'vtk.js/Sources/Common/DataModel/ImageData';
2+
import vtkDataArray from 'vtk.js/Sources/Common/Core/DataArray';
3+
4+
/**
5+
* Takes a canvas and converts it to a vtkImageData.
6+
*
7+
* Optionally supply a bounding box to get a particular subset of the canvas.
8+
*
9+
* @param canvas The HTML canvas to convert
10+
* @param boundingBox A bounding box of type [top, left, width, height]
11+
*/
12+
function canvasToImageData(canvas, boundingBox = [0, 0, 0, 0]) {
13+
const [top, left, width, height] = boundingBox;
14+
const ctxt = canvas.getContext('2d');
15+
const idata = ctxt.getImageData(top, left, width || canvas.width, height || canvas.height);
16+
17+
const imageData = vtkImageData.newInstance({ type: 'vtkImageData' });
18+
imageData.setOrigin(0, 0, 0);
19+
imageData.setSpacing(1, 1, 1);
20+
imageData.setExtent(0, canvas.width - 1, 0, canvas.height - 1, 0, 0);
21+
22+
const scalars = vtkDataArray.newInstance({ numberOfComponents: 4, values: idata.data });
23+
scalars.setName('scalars');
24+
imageData.getPointData().setScalars(scalars);
25+
26+
return imageData;
27+
}
28+
29+
/**
30+
* Converts an Image object to a vtkImageData.
31+
*/
32+
function imageToImageData(image) {
33+
const canvas = document.createElement('canvas');
34+
canvas.width = image.width;
35+
canvas.height = image.height;
36+
canvas.getContext('2d').drawImage(image, 0, 0);
37+
return canvasToImageData(canvas);
38+
}
39+
40+
export default { canvasToImageData, imageToImageData };
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const Corners = {
2+
TOP_LEFT: 'TOP_LEFT',
3+
TOP_RIGHT: 'TOP_RIGHT',
4+
BOTTOM_LEFT: 'BOTTOM_LEFT',
5+
BOTTOM_RIGHT: 'BOTTOM_RIGHT',
6+
};
7+
8+
9+
export default { Corners };
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
### setEnabled(enabling)
2+
3+
Sets the widget enabled status, i.e. to show the widget or not.
4+
5+
### setViewportCorner(vtkOrientationMarkerWidget.Corner)
6+
7+
Sets which corner to put the widget's viewport. Defaults to
8+
BOTTOM_LEFT.
9+
10+
### setViewportSize(sizeFactor)
11+
12+
Sets the viewport size. The sizeFactor should be between 0.0 and 1.0.
13+
It says how much of the main render window to color. Defaults to 0.2.
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import 'vtk.js/Sources/favicon';
2+
3+
import vtkFullScreenRenderWindow from 'vtk.js/Sources/Rendering/Misc/FullScreenRenderWindow';
4+
import vtkActor from 'vtk.js/Sources/Rendering/Core/Actor';
5+
import vtkConeSource from 'vtk.js/Sources/Filters/Sources/ConeSource';
6+
import vtkMapper from 'vtk.js/Sources/Rendering/Core/Mapper';
7+
import vtkOrientationMarkerWidget from 'vtk.js/Sources/Interaction/Widgets/OrientationMarkerWidget';
8+
import vtkAnnotatedCubeActor from 'vtk.js/Sources/Rendering/Core/AnnotatedCubeActor/index.js';
9+
10+
// ----------------------------------------------------------------------------
11+
// Standard rendering code setup
12+
// ----------------------------------------------------------------------------
13+
14+
const fullScreenRenderer = vtkFullScreenRenderWindow.newInstance({ background: [0.2, 0.2, 0.2] });
15+
const renderer = fullScreenRenderer.getRenderer();
16+
const renderWindow = fullScreenRenderer.getRenderWindow();
17+
18+
// ----------------------------------------------------------------------------
19+
// Example code
20+
// ----------------------------------------------------------------------------
21+
22+
// create cone
23+
const coneSource = vtkConeSource.newInstance();
24+
const actor = vtkActor.newInstance();
25+
const mapper = vtkMapper.newInstance();
26+
27+
actor.setMapper(mapper);
28+
mapper.setInputConnection(coneSource.getOutputPort());
29+
30+
renderer.addActor(actor);
31+
32+
// create axes
33+
const axes = vtkAnnotatedCubeActor.newInstance();
34+
axes.setDefaultStyle({
35+
text: '+X',
36+
fontStyle: 'bold',
37+
fontFamily: 'Arial',
38+
fontColor: 'black',
39+
fontSizeScale: res => res / 2,
40+
faceColor: '#0000ff',
41+
edgeThickness: 0.1,
42+
edgeColor: 'black',
43+
resolution: 400,
44+
});
45+
// axes.setXPlusFaceProperty({ text: '+X' });
46+
axes.setXMinusFaceProperty({ text: '-X', faceColor: '#ffff00', fontStyle: 'italic' });
47+
axes.setYPlusFaceProperty({ text: '+Y', faceColor: '#00ff00', fontSizeScale: res => res / 4 });
48+
axes.setYMinusFaceProperty({ text: '-Y', faceColor: '#00ffff', fontColor: 'white' });
49+
axes.setZPlusFaceProperty({ text: '+Z', edgeColor: 'yellow' });
50+
axes.setZMinusFaceProperty({ text: '-Z', edgeThickness: 0 });
51+
52+
// create orientation widget
53+
const orientationWidget = vtkOrientationMarkerWidget.newInstance({
54+
actor: axes,
55+
interactor: renderWindow.getInteractor(),
56+
});
57+
orientationWidget.setEnabled(true);
58+
orientationWidget.setViewportCorner(vtkOrientationMarkerWidget.Corners.BOTTOM_RIGHT);
59+
orientationWidget.setViewportSize(0.15);
60+
61+
renderer.resetCamera();
62+
renderWindow.render();
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import macro from 'vtk.js/Sources/macro';
2+
import vtkRenderer from 'vtk.js/Sources/Rendering/Core/Renderer';
3+
import Constants from 'vtk.js/Sources/Interaction/Widgets/OrientationMarkerWidget/Constants';
4+
5+
// ----------------------------------------------------------------------------
6+
// vtkOrientationMarkerWidget
7+
// ----------------------------------------------------------------------------
8+
9+
// depends on Constants.Corners
10+
const VIEWPORTS = {
11+
TOP_LEFT: size => [0, (1 - size), size, 1],
12+
TOP_RIGHT: size => [(1 - size), (1 - size), 1, 1],
13+
BOTTOM_LEFT: size => [0, 0, size, size],
14+
BOTTOM_RIGHT: size => [(1 - size), 0, 1, size],
15+
};
16+
17+
function vtkOrientationMarkerWidget(publicAPI, model) {
18+
// Set our className
19+
model.classHierarchy.push('vtkOrientationMarkerWidget');
20+
21+
// Private variables
22+
23+
const selfRenderer = vtkRenderer.newInstance();
24+
let interactorUnsubscribe = null;
25+
26+
// private methods
27+
28+
function updateMarkerOrientation() {
29+
const currentCamera = model.interactor.findPokedRenderer().getActiveCamera();
30+
31+
if (!currentCamera) {
32+
return;
33+
}
34+
35+
// window.camera = currentCamera;
36+
const state = currentCamera.get('position', 'focalPoint', 'viewUp');
37+
selfRenderer.getActiveCamera().set(state);
38+
selfRenderer.resetCamera();
39+
}
40+
41+
function getViewport() {
42+
return VIEWPORTS[model.viewportCorner](model.viewportSize);
43+
}
44+
45+
// public methods
46+
47+
/**
48+
* Enables/Disables the orientation marker.
49+
*/
50+
publicAPI.setEnabled = (enabling) => {
51+
if (enabling) {
52+
if (model.enabled) {
53+
return;
54+
}
55+
56+
if (!model.actor) {
57+
console.error('Must set actor before enabling orientation marker.');
58+
return;
59+
}
60+
61+
if (!model.interactor) {
62+
console.error('Must set interactor before enabling orientation marker.');
63+
return;
64+
}
65+
66+
const renderWindow = model.interactor.findPokedRenderer().getRenderWindow();
67+
renderWindow.addRenderer(selfRenderer);
68+
if (renderWindow.getNumberOfLayers() < 2) {
69+
renderWindow.setNumberOfLayers(2);
70+
}
71+
// Highest number is foreground
72+
selfRenderer.setLayer(renderWindow.getNumberOfLayers() - 1);
73+
selfRenderer.setInteractive(false);
74+
75+
selfRenderer.addViewProp(model.actor);
76+
model.actor.setVisibility(true);
77+
78+
selfRenderer.setViewport(...getViewport());
79+
80+
const { unsubscribe } = model.interactor.onAnimation(updateMarkerOrientation);
81+
interactorUnsubscribe = unsubscribe;
82+
83+
model.enabled = true;
84+
} else {
85+
if (!model.enabled) {
86+
return;
87+
}
88+
model.enabled = false;
89+
90+
interactorUnsubscribe();
91+
interactorUnsubscribe = null;
92+
93+
model.actor.setVisibility(false);
94+
selfRenderer.removeViewProp(model.actor);
95+
96+
const renderWindow = model.interactor.findPokedRenderer().getRenderWindow();
97+
if (renderWindow) {
98+
renderWindow.removeRenderer(selfRenderer);
99+
}
100+
}
101+
};
102+
103+
/**
104+
* Sets the viewport corner.
105+
*/
106+
publicAPI.setViewportCorner = (corner) => {
107+
model.viewportCorner = corner;
108+
if (model.enabled) {
109+
selfRenderer.setViewport(...getViewport());
110+
}
111+
};
112+
113+
/**
114+
* Sets the viewport size.
115+
*/
116+
publicAPI.setViewportSize = (sizeFactor) => {
117+
model.viewportSize = Math.min(1, Math.max(0, sizeFactor));
118+
if (model.enabled) {
119+
selfRenderer.setViewport(...getViewport());
120+
}
121+
};
122+
}
123+
124+
// ----------------------------------------------------------------------------
125+
// Object factory
126+
// ----------------------------------------------------------------------------
127+
128+
export const DEFAULT_VALUES = {
129+
// actor: null,
130+
// interactor: null,
131+
viewportCorner: Constants.Corners.BOTTOM_LEFT,
132+
viewportSize: 0.2,
133+
};
134+
135+
// ----------------------------------------------------------------------------
136+
137+
export function extend(publicAPI, model, initialValues = {}) {
138+
Object.assign(model, DEFAULT_VALUES, initialValues);
139+
140+
// Build VTK API
141+
macro.obj(publicAPI, model);
142+
143+
macro.get(publicAPI, model, [
144+
'enabled',
145+
'viewportCorner',
146+
'viewportSize',
147+
]);
148+
149+
// NOTE: setting these while the widget is enabled will
150+
// not update the widget.
151+
macro.setGet(publicAPI, model, [
152+
'actor',
153+
'interactor',
154+
]);
155+
156+
// Object methods
157+
vtkOrientationMarkerWidget(publicAPI, model);
158+
}
159+
160+
// ----------------------------------------------------------------------------
161+
162+
export const newInstance = macro.newInstance(extend, 'vtkOrientationMarkerWidget');
163+
164+
// ----------------------------------------------------------------------------
165+
166+
export default Object.assign({ newInstance, extend }, Constants);
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
### setResolution(resolution)
2+
3+
Sets the cube face resolution. Defaults to 200.
4+
5+
### setFontStyle(style)
6+
7+
Sets the font style. This is any valid CSS font style, e.g. normal,
8+
bold, etc. Defaults to "normal".
9+
10+
### setFontFamily(family)
11+
12+
Sets the font family. This is any valid CSS font family name. Defaults
13+
to "Arial".
14+
15+
### setFontColor(color)
16+
17+
Sets the font color. This is any valid CSS color. Defaults to "black".
18+
19+
### setEdgeThickness(thickness)
20+
21+
Edge thickness is a value between 0.0 and 1.0, and represents the
22+
fraction of the face resolution to cover (for one edge). Defaults to
23+
0.1.
24+
25+
If the thickness is 0, then no edge is rendered.
26+
27+
### setEdgeColor(color)
28+
29+
Sets the edge color. Defaults to black.
30+
31+
### setXPlusFaceProperty({ text, faceColor})
32+
33+
Sets the +X face property.
34+
35+
This takes an object, where you can optionally set the face text
36+
or the face color, e.g. { text: 'Text', faceColor: '#0000ff' }.
37+
38+
### setXMinusFaceProperty({ text, faceColor})
39+
40+
Sets the -X face property.
41+
42+
This takes an object, where you can optionally set the face text
43+
or the face color, e.g. { text: 'Text', faceColor: '#0000ff' }.
44+
45+
### setYPlusFaceProperty({ text, faceColor})
46+
47+
Sets the +Y face property.
48+
49+
This takes an object, where you can optionally set the face text
50+
or the face color, e.g. { text: 'Text', faceColor: '#0000ff' }.
51+
52+
### setYMinusFaceProperty({ text, faceColor})
53+
54+
Sets the -Y face property.
55+
56+
This takes an object, where you can optionally set the face text
57+
or the face color, e.g. { text: 'Text', faceColor: '#0000ff' }.
58+
59+
### setZPlusFaceProperty({ text, faceColor})
60+
61+
Sets the +Z face property.
62+
63+
This takes an object, where you can optionally set the face text
64+
or the face color, e.g. { text: 'Text', faceColor: '#0000ff' }.
65+
66+
### setZMinusFaceProperty({ text, faceColor})
67+
68+
Sets the -Z face property.
69+
70+
This takes an object, where you can optionally set the face text
71+
or the face color, e.g. { text: 'Text', faceColor: '#0000ff' }.

0 commit comments

Comments
 (0)