Skip to content

Commit 42e8fca

Browse files
committed
fix(LineGlyphRepresentation): extend GlyphRepresentation
1 parent c035dec commit 42e8fca

File tree

1 file changed

+104
-159
lines changed
  • Sources/Widgets/Representations/LineGlyphRepresentation

1 file changed

+104
-159
lines changed

Sources/Widgets/Representations/LineGlyphRepresentation/index.js

Lines changed: 104 additions & 159 deletions
Original file line numberDiff line numberDiff line change
@@ -1,188 +1,133 @@
11
import macro from 'vtk.js/Sources/macros';
22
import * as vtkMath from 'vtk.js/Sources/Common/Core/Math';
3-
import vtkBoundingBox from 'vtk.js/Sources/Common/DataModel/BoundingBox';
4-
import { getPixelWorldHeightAtCoord } from 'vtk.js/Sources/Widgets/Core/WidgetManager';
5-
import vtkWidgetRepresentation, {
6-
allocateArray,
7-
} from 'vtk.js/Sources/Widgets/Representations/WidgetRepresentation';
8-
import { RenderingTypes } from 'vtk.js/Sources/Widgets/Core/WidgetManager/Constants';
9-
import vtkPolyData from 'vtk.js/Sources/Common/DataModel/PolyData';
10-
import vtkActor from 'vtk.js/Sources/Rendering/Core/Actor';
113
import vtkGlyph3DMapper from 'vtk.js/Sources/Rendering/Core/Glyph3DMapper';
12-
import {
13-
OrientationModes,
14-
ScaleModes,
15-
} from 'vtk.js/Sources/Rendering/Core/Glyph3DMapper/Constants';
4+
import vtkGlyphRepresentation from 'vtk.js/Sources/Widgets/Representations/GlyphRepresentation';
5+
import { Behavior } from 'vtk.js/Sources/Widgets/Representations/WidgetRepresentation/Constants';
6+
import { allocateArray } from 'vtk.js/Sources/Widgets/Representations/WidgetRepresentation';
167
import vtkCylinderSource from 'vtk.js/Sources/Filters/Sources/CylinderSource';
17-
import { vec3 } from 'gl-matrix';
8+
import { OrientationModes } from '@kitware/vtk.js/Rendering/Core/Glyph3DMapper/Constants';
9+
import { getPixelWorldHeightAtCoord } from 'vtk.js/Sources/Widgets/Core/WidgetManager';
1810

1911
function vtkLineGlyphRepresentation(publicAPI, model) {
2012
model.classHierarchy.push('vtkLineGlyphRepresentation');
21-
const superClass = { ...publicAPI };
22-
23-
const internalPolyData = vtkPolyData.newInstance({ mtime: 0 });
24-
25-
function allocateSize(polyData, pointCount, close = false) {
26-
const glyphCount = pointCount + (close ? 0 : -1);
27-
if (
28-
!polyData.getPoints() ||
29-
polyData.getPoints().length !== glyphCount * 3
30-
) {
31-
allocateArray(polyData, 'points', glyphCount).getData();
32-
}
33-
34-
const cellSize = glyphCount + 1;
35-
const oldSize = Array.from(polyData.getLines().getCellSizes())[0];
36-
if (oldSize !== cellSize) {
37-
const lines = allocateArray(polyData, 'lines', cellSize + 1); // +1 for to hold number of elements per cell
38-
const cellArray = lines.getData();
39-
cellArray[0] = cellSize;
40-
for (let i = 1; i <= cellSize; i++) {
41-
cellArray[i] = i - 1;
42-
}
43-
if (close) {
44-
cellArray[cellSize] = 0;
45-
}
46-
lines.setData(cellArray);
47-
}
48-
}
49-
50-
/**
51-
* Change the segments' thickness.
52-
* @param {number} lineThickness
53-
*/
54-
function applyLineThickness(lineThickness) {
55-
let scaledLineThickness = lineThickness;
56-
if (publicAPI.getScaleInPixels() && internalPolyData) {
57-
const center = vtkBoundingBox.getCenter(internalPolyData.getBounds());
58-
scaledLineThickness *= getPixelWorldHeightAtCoord(
59-
center,
60-
model.displayScaleParams
61-
);
62-
}
63-
model._pipeline.glyph.setRadius(scaledLineThickness);
64-
}
65-
66-
model._pipeline = {
67-
source: publicAPI,
68-
glyph: vtkCylinderSource.newInstance({
69-
direction: [1, 0, 0],
70-
center: [0.5, 0, 0],
71-
capping: false,
72-
}),
73-
mapper: vtkGlyph3DMapper.newInstance({
74-
orientationArray: 'directions',
75-
orientationMode: OrientationModes.DIRECTION,
76-
scaleArray: 'lengths',
77-
scaleMode: ScaleModes.SCALE_BY_COMPONENTS,
78-
}),
79-
actor: vtkActor.newInstance({ parentProp: publicAPI }),
80-
};
81-
82-
vtkWidgetRepresentation.connectPipeline(model._pipeline);
83-
publicAPI.addActor(model._pipeline.actor);
8413

85-
// --------------------------------------------------------------------------
86-
publicAPI.requestData = (inData, outData) => {
87-
const state = inData[0];
88-
outData[0] = internalPolyData;
89-
90-
const originStates = publicAPI.getRepresentationStates(state);
91-
const points = originStates
92-
.map((subState) => subState.getOrigin())
93-
.filter(Boolean); // filter out states that return invalid origins
94-
const pointCount = points.length;
95-
96-
allocateSize(internalPolyData, pointCount, model.close && pointCount > 2);
97-
98-
const glyphPositions = internalPolyData.getPoints().getData();
99-
// There can be only one line.
100-
const segments = internalPolyData.getLines().getData();
14+
publicAPI.setGlyphResolution = macro.chain(
15+
publicAPI.setGlyphResolution,
16+
model._pipeline.glyph.setResolution
17+
);
18+
}
10119

102-
const directions = allocateArray(
103-
internalPolyData,
104-
'directions',
105-
segments.length - 1,
106-
undefined,
107-
3
108-
).getData();
109-
const lengths = allocateArray(
110-
internalPolyData,
111-
'lengths',
112-
segments.length - 1,
113-
undefined,
20+
function cylinderScale(publicAPI, model) {
21+
return (polyData, states) => {
22+
model._pipeline.mapper.setScaleArray('scale');
23+
model._pipeline.mapper.setScaleFactor(1);
24+
model._pipeline.mapper.setScaling(true);
25+
model._pipeline.mapper.setScaleMode(
26+
vtkGlyph3DMapper.ScaleModes.SCALE_BY_COMPONENTS
27+
);
28+
const scales = allocateArray(
29+
polyData,
30+
'scale',
31+
states.length,
32+
'Float32Array',
11433
3
11534
).getData();
116-
117-
const tempVector = []; // scratch
118-
for (let point = 1; point < segments.length - 1; point++) {
119-
const glyph = (point - 1) * 3; // start of glyph's 3 components in the arrays
120-
121-
// With cylinder glyph's offset center, position at state origins.
122-
const origin = points[segments[point]];
123-
[
124-
glyphPositions[glyph],
125-
glyphPositions[glyph + 1],
126-
glyphPositions[glyph + 2],
127-
] = origin;
128-
129-
// Orient glyphs to next point.
130-
const target = points[segments[point + 1]];
131-
const direction = vtkMath.subtract(target, origin, tempVector);
132-
[directions[glyph], directions[glyph + 1], directions[glyph + 2]] =
133-
direction;
134-
135-
// Scale to span between points.
136-
const distance = vec3.length(direction);
137-
lengths[glyph] = distance;
138-
lengths[glyph + 1] = 1;
139-
lengths[glyph + 2] = 1;
35+
let j = 0;
36+
for (let i = 0; i < states.length; ++i) {
37+
const state = states[i];
38+
const origin = state.getOrigin();
39+
const nextOrigin =
40+
states[i === states.length - 1 ? 0 : i + 1].getOrigin();
41+
42+
const direction = vtkMath.subtract(nextOrigin, origin, []);
43+
const length = vtkMath.normalize(direction);
44+
45+
let scaleFactor = state.getActive() ? model.activeScaleFactor : 1;
46+
if (publicAPI.getScaleInPixels()) {
47+
scaleFactor *= getPixelWorldHeightAtCoord(
48+
state.getOrigin(),
49+
model.displayScaleParams
50+
);
51+
}
52+
const scale = state.getScale3?.() ?? [
53+
model.lineThickness,
54+
model.lineThickness,
55+
1,
56+
];
57+
scales[j++] = length * scale[0];
58+
scales[j++] = scaleFactor * scale[1];
59+
scales[j++] = scaleFactor * scale[2];
14060
}
141-
142-
internalPolyData.getPoints().modified();
143-
internalPolyData.modified();
144-
145-
const lineThickness = state.getLineThickness?.() ?? model.lineThickness;
146-
applyLineThickness(lineThickness);
14761
};
62+
}
14863

149-
// return array of all states
150-
publicAPI.getSelectedState = () => model.inputData[0];
151-
152-
publicAPI.updateActorVisibility = (renderingType, ctxVisible, hVisible) => {
153-
const state = model.inputData[0];
154-
155-
// Make lines/tubes thicker for picking
156-
let lineThickness = state.getLineThickness?.() ?? model.lineThickness;
157-
if (renderingType === RenderingTypes.PICKING_BUFFER) {
158-
lineThickness = Math.max(4, lineThickness);
64+
function cylinderDirection(publicAPI, model) {
65+
return (polyData, states) => {
66+
model._pipeline.mapper.setOrientationArray('orientation');
67+
model._pipeline.mapper.setOrientationMode(OrientationModes.MATRIX);
68+
const orientation = allocateArray(
69+
polyData,
70+
'orientation',
71+
states.length,
72+
'Float32Array',
73+
9
74+
).getData();
75+
for (let i = 0; i < states.length; ++i) {
76+
const state = states[i];
77+
const origin = state.getOrigin();
78+
const nextOrigin =
79+
states[i === states.length - 1 ? 0 : i + 1].getOrigin();
80+
81+
const direction = vtkMath.subtract(nextOrigin, origin, []);
82+
vtkMath.normalize(direction);
83+
const right = [1, 0, 0];
84+
const up = [0, 1, 0];
85+
vtkMath.perpendiculars(direction, up, right, 0);
86+
87+
orientation.set(direction, 9 * i);
88+
orientation.set(up, 9 * i + 3);
89+
orientation.set(right, 9 * i + 6);
15990
}
160-
applyLineThickness(lineThickness);
161-
162-
return superClass.updateActorVisibility(
163-
renderingType,
164-
ctxVisible,
165-
hVisible
166-
);
16791
};
16892
}
16993

17094
// ----------------------------------------------------------------------------
17195
// Object factory
17296
// ----------------------------------------------------------------------------
17397

174-
const DEFAULT_VALUES = {
175-
close: false,
176-
lineThickness: 1,
177-
scaleInPixels: true,
178-
};
98+
function defaultValues(publicAPI, model, initialValues) {
99+
return {
100+
behavior: Behavior.CONTEXT,
101+
glyphResolution: 32,
102+
lineThickness: 1,
103+
...initialValues,
104+
_pipeline: {
105+
glyph:
106+
initialValues?.pipeline?.glyph ??
107+
vtkCylinderSource.newInstance({
108+
direction: [1, 0, 0],
109+
center: [0.5, 0, 0],
110+
capping: false,
111+
}),
112+
...initialValues?.pipeline,
113+
},
114+
applyMixin: {
115+
noScale: cylinderScale(publicAPI, model),
116+
noOrientation: cylinderDirection(publicAPI, model),
117+
},
118+
};
119+
}
179120

180121
// ----------------------------------------------------------------------------
181122

182123
export function extend(publicAPI, model, initialValues = {}) {
183-
const newDefault = { ...DEFAULT_VALUES, ...initialValues };
184-
vtkWidgetRepresentation.extend(publicAPI, model, newDefault);
185-
macro.setGet(publicAPI, model, ['close', 'lineThickness']);
124+
vtkGlyphRepresentation.extend(
125+
publicAPI,
126+
model,
127+
defaultValues(publicAPI, model, initialValues)
128+
);
129+
macro.setGet(publicAPI, model, ['glyphResolution', 'lineThickness']);
130+
macro.get(publicAPI, model._pipeline, ['glyph', 'mapper', 'actor']);
186131

187132
vtkLineGlyphRepresentation(publicAPI, model);
188133
}

0 commit comments

Comments
 (0)