Skip to content

Commit 77e366d

Browse files
committed
feat(SegmentedLineRepresentation): orient glyphs to next point
1 parent ac619e4 commit 77e366d

File tree

2 files changed

+73
-69
lines changed
  • Sources/Widgets/Representations/SegmentedLineRepresentation

2 files changed

+73
-69
lines changed

Sources/Widgets/Representations/SegmentedLineRepresentation/example/index.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ const compositeState = vtkStateBuilder
3434
const z = -50;
3535
const points = [
3636
[0, 0, z],
37-
[5, 0, z],
38-
[5, 10, z],
37+
[5, 5, z],
38+
[3, 10, z],
39+
[5, 15, z],
3940
];
4041
points.forEach((point) => {
4142
const handle = compositeState.addHandle();
@@ -48,6 +49,7 @@ points.forEach((point) => {
4849

4950
const widgetRep = vtkSegmentedLineRepresentation.newInstance({
5051
scaleInPixels: false,
52+
// closePolyLine: true,
5153
});
5254
widgetRep.setInputData(compositeState);
5355
widgetRep.setLabels(['handles']);

Sources/Widgets/Representations/SegmentedLineRepresentation/index.js

Lines changed: 69 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,50 @@
11
import macro from 'vtk.js/Sources/macros';
2-
import vtkActor from 'vtk.js/Sources/Rendering/Core/Actor';
3-
import vtkMapper from 'vtk.js/Sources/Rendering/Core/Mapper';
42
import * as vtkMath from 'vtk.js/Sources/Common/Core/Math';
53
import vtkBoundingBox from 'vtk.js/Sources/Common/DataModel/BoundingBox';
6-
import vtkTubeFilter from 'vtk.js/Sources/Filters/General/TubeFilter';
74
import { getPixelWorldHeightAtCoord } from 'vtk.js/Sources/Widgets/Core/WidgetManager';
85
import vtkWidgetRepresentation, {
96
allocateArray,
107
} from 'vtk.js/Sources/Widgets/Representations/WidgetRepresentation';
118
import { RenderingTypes } from 'vtk.js/Sources/Widgets/Core/WidgetManager/Constants';
129
import vtkPolyData from 'vtk.js/Sources/Common/DataModel/PolyData';
10+
import vtkActor from 'vtk.js/Sources/Rendering/Core/Actor';
11+
import vtkGlyph3DMapper from 'vtk.js/Sources/Rendering/Core/Glyph3DMapper';
12+
import { OrientationModes } from 'vtk.js/Sources/Rendering/Core/Glyph3DMapper/Constants';
13+
import vtkCylinderSource from 'vtk.js/Sources/Filters/Sources/CylinderSource';
1314

1415
// ----------------------------------------------------------------------------
1516
// vtkPolyLineRepresentation methods
1617
// ----------------------------------------------------------------------------
1718

1819
function vtkPolyLineRepresentation(publicAPI, model) {
19-
// Set our className
2020
model.classHierarchy.push('vtkPolyLineRepresentation');
2121
const superClass = { ...publicAPI };
2222

23-
// --------------------------------------------------------------------------
24-
// Internal polydata dataset
25-
// --------------------------------------------------------------------------
2623
const internalPolyData = vtkPolyData.newInstance({ mtime: 0 });
2724

2825
function allocateSize(polyData, size, closePolyLine = false) {
29-
let points = null;
30-
if (size < 2) {
31-
// FIXME: Why 1 point and not 0 ?
32-
points = allocateArray(polyData, 'points', 1).getData();
33-
points.set([0, 0, 0]);
34-
allocateArray(polyData, 'lines', 0).getData();
35-
} else if (
36-
!polyData.getPoints() ||
37-
polyData.getPoints().length !== size * 3
26+
if (!polyData.getPoints() || polyData.getPoints().length !== size * 3) {
27+
allocateArray(polyData, 'points', size).getData();
28+
}
29+
30+
const cellSize = size + (closePolyLine ? 1 : 0);
31+
if (
32+
polyData.getLines().getNumberOfCells() !== 1 ||
33+
polyData.getLines().getCellSizes()[0] !== cellSize
3834
) {
39-
points = allocateArray(polyData, 'points', size).getData();
40-
const cellSize = size + (closePolyLine ? 1 : 0);
41-
if (
42-
polyData.getLines().getNumberOfCells() !== 1 ||
43-
polyData.getLines().getCellSizes()[0] !== cellSize
44-
) {
45-
const lines = allocateArray(polyData, 'lines', cellSize + 1); // +1 for the number of points
46-
const cellData = lines.getData();
47-
cellData[0] = cellSize;
48-
for (let i = 1; i <= cellSize; i++) {
49-
cellData[i] = i - 1;
50-
}
51-
if (closePolyLine) {
52-
cellData[cellSize] = 0;
53-
}
54-
lines.setData(cellData);
35+
const lines = allocateArray(polyData, 'lines', cellSize + 1).getData(); // +1 for the number of points
36+
lines[0] = cellSize;
37+
for (let i = 1; i <= cellSize; i++) {
38+
lines[i] = i - 1;
39+
}
40+
if (closePolyLine) {
41+
lines[cellSize] = 0;
5542
}
5643
}
57-
return points;
5844
}
5945

6046
/**
61-
* Change the line/tube thickness.
47+
* Change the segments thickness.
6248
* @param {number} lineThickness
6349
*/
6450
function applyLineThickness(lineThickness) {
@@ -70,35 +56,30 @@ function vtkPolyLineRepresentation(publicAPI, model) {
7056
model.displayScaleParams
7157
);
7258
}
73-
model._pipelines.tubes.filter.setRadius(scaledLineThickness);
59+
model._pipeline.glyph.setRadius(scaledLineThickness);
7460
}
7561

76-
// --------------------------------------------------------------------------
77-
// Generic rendering pipeline
78-
// --------------------------------------------------------------------------
79-
80-
model._pipelines = {
81-
tubes: {
82-
source: publicAPI,
83-
filter: vtkTubeFilter.newInstance({
84-
radius: model.lineThickness,
85-
numberOfSides: 12,
86-
capping: false,
87-
}),
88-
mapper: vtkMapper.newInstance(),
89-
actor: vtkActor.newInstance({ parentProp: publicAPI }),
90-
},
62+
model._pipeline = {
63+
source: publicAPI,
64+
glyph: vtkCylinderSource.newInstance({
65+
direction: [1, 0, 0],
66+
}),
67+
mapper: vtkGlyph3DMapper.newInstance({
68+
orientationArray: 'directions',
69+
orientationMode: OrientationModes.DIRECTION,
70+
}),
71+
actor: vtkActor.newInstance({ parentProp: publicAPI }),
9172
};
9273

93-
vtkWidgetRepresentation.connectPipeline(model._pipelines.tubes);
94-
publicAPI.addActor(model._pipelines.tubes.actor);
74+
vtkWidgetRepresentation.connectPipeline(model._pipeline);
75+
publicAPI.addActor(model._pipeline.actor);
9576

9677
// --------------------------------------------------------------------------
9778
publicAPI.requestData = (inData, outData) => {
9879
const state = inData[0];
9980
outData[0] = internalPolyData;
10081

101-
// Remove invalid and coincident points for tube filter.
82+
// Remove invalid and coincident points.
10283
const list = publicAPI
10384
.getRepresentationStates(state)
10485
.reduce((subStates, subState) => {
@@ -120,23 +101,44 @@ function vtkPolyLineRepresentation(publicAPI, model) {
120101
}, []);
121102
const size = list.length;
122103

123-
const points = allocateSize(
124-
outData[0],
125-
size,
126-
model.closePolyLine && size > 2
127-
);
104+
allocateSize(internalPolyData, size, model.closePolyLine && size > 2);
128105

129-
if (points) {
130-
for (let i = 0; i < size; i++) {
131-
const coords = list[i].getOrigin();
132-
points[i * 3] = coords[0];
133-
points[i * 3 + 1] = coords[1];
134-
points[i * 3 + 2] = coords[2];
135-
}
106+
const points = internalPolyData.getPoints().getData();
107+
const lines = internalPolyData.getLines().getData();
108+
109+
for (let i = 0; i < size; i++) {
110+
const coords = list[i].getOrigin();
111+
points[i * 3] = coords[0];
112+
points[i * 3 + 1] = coords[1];
113+
points[i * 3 + 2] = coords[2];
114+
}
115+
116+
// Orient glyphs to next point.
117+
const directions = allocateArray(
118+
internalPolyData,
119+
'directions',
120+
lines.length - 1,
121+
undefined,
122+
3
123+
).getData();
124+
for (let point = 1; point < lines.length - 1; point++) {
125+
const eye = lines[point] * 3;
126+
const eyePoint = [points[eye], points[eye + 1], points[eye + 2]];
127+
const target = lines[point + 1] * 3;
128+
const targetPoint = [
129+
points[target],
130+
points[target + 1],
131+
points[target + 2],
132+
];
133+
const direction = vtkMath.subtract(targetPoint, eyePoint, []);
134+
const glyph = (point - 1) * 3;
135+
directions[glyph] = direction[0];
136+
directions[glyph + 1] = direction[1];
137+
directions[glyph + 2] = direction[2];
136138
}
137139

138-
outData[0].getPoints().modified();
139-
outData[0].modified();
140+
internalPolyData.getPoints().modified();
141+
internalPolyData.modified();
140142

141143
const lineThickness = state.getLineThickness?.() ?? model.lineThickness;
142144
applyLineThickness(lineThickness);

0 commit comments

Comments
 (0)