Skip to content

Commit 2f283ed

Browse files
committed
feat(TubeFilter): Added TCoords support to tube filter
1 parent ce3f2a9 commit 2f283ed

File tree

3 files changed

+157
-20
lines changed

3 files changed

+157
-20
lines changed

Sources/Filters/General/TubeFilter/Constants.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ export const VtkVaryRadius = {
55
VARY_RADIUS_BY_ABSOLUTE_SCALAR: 3,
66
};
77

8+
export const VtkTCoords = {
9+
TCOORDS_OFF: 0, // default
10+
TCOORDS_FROM_NORMALIZED_LENGTH: 1,
11+
TCOORDS_FROM_LENGTH: 2,
12+
TCOORDS_FROM_SCALARS: 3,
13+
};
14+
815
export default {
916
VtkVaryRadius,
17+
VtkTCoords,
1018
};

Sources/Filters/General/TubeFilter/example/index.js

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -56,24 +56,24 @@ function initializePolyData(dType) {
5656
const polyData = vtkPolyData.newInstance();
5757
const points = vtkPoints.newInstance({ dataType: pointType });
5858
points.setNumberOfPoints(numSegments + 1);
59-
const pointData = points.getData();
59+
// const pointData = points.getData();
6060
const verts = new Uint32Array(2 * (numSegments + 1));
6161
const lines = new Uint32Array(numSegments + 2);
6262
lines[0] = numSegments + 1;
6363

64+
const pointData = [0, 0, 0, -0.13, -0.51, 0, -0.41, -0.48, 0];
65+
points.setData(pointData);
6466
for (let i = 0; i < (numSegments + 1); ++i) {
65-
for (let j = 0; j < 3; ++j) {
66-
pointData[(3 * i) + j] = Math.random();
67-
}
68-
console.log(`${pointData[3 * i]}, ${pointData[(3 * i) + 1]}, ${pointData[(3 * i) + 2]}`);
67+
// for (let j = 0; j < 3; ++j) {
68+
// pointData[(3 * i) + j] = Math.random();
69+
// }
6970
verts[i] = 1;
7071
verts[i + 1] = i;
7172
lines[i + 1] = i;
72-
console.log(`Line = ${lines[i + 1]}`);
7373
}
7474

7575
polyData.setPoints(points);
76-
polyData.getVerts().setData(verts);
76+
// polyData.getVerts().setData(verts);
7777
polyData.getLines().setData(lines);
7878
return polyData;
7979
}
@@ -84,8 +84,10 @@ function initializePolyData(dType) {
8484
// const pointSource = vtkPointSource.newInstance({ numberOfPoints: 25, radius: 0.25 });
8585
const polyData = initializePolyData(VtkPointPrecision.DOUBLE);
8686
const tubeFilter = vtkTubeFilter.newInstance();
87-
tubeFilter.setCapping(false);
88-
tubeFilter.setNumberOfSides(20);
87+
tubeFilter.setCapping(true);
88+
tubeFilter.setNumberOfSides(30);
89+
tubeFilter.setRadius(0.083);
90+
tubeFilter.setRadiusFactor(10);
8991

9092
tubeFilter.setInputData(polyData);
9193

Sources/Filters/General/TubeFilter/index.js

Lines changed: 138 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { VtkPointPrecision } from 'vtk.js/Sources/Filters/General/Constants';
1010

1111
import Constants from './Constants';
1212

13-
const { VtkVaryRadius } = Constants;
13+
const { VtkVaryRadius, VtkTCoords } = Constants;
1414
const { vtkDebugMacro, vtkErrorMacro, vtkWarningMacro } = macro;
1515

1616
// ----------------------------------------------------------------------------
@@ -52,7 +52,7 @@ function vtkTubeFilter(publicAPI, model) {
5252
}
5353

5454
function generateSlidingNormals(pts, lines, normals, firstNormal = null) {
55-
var normal = [0.0, 0.0, 0.0];
55+
var normal = [0.0, 0.0, 1.0];
5656
const lineData = lines;
5757
// lid = 0;
5858
let npts = lineData[0];
@@ -215,45 +215,54 @@ function vtkTubeFilter(publicAPI, model) {
215215
sPrev[i] = sNext[i];
216216
startCapNorm[i] = -sPrev[i];
217217
}
218+
console.log(`First Point: ${p}`);
219+
console.log(`startCapNorm: ${startCapNorm}`);
218220
vtkMath.normalize(startCapNorm);
221+
console.log(`startCapNorm: ${startCapNorm}`);
219222
} else if (j === (npts - 1)) {
220223
for (let i = 0; i < 3; ++i) {
221224
sPrev[i] = sNext[i];
222225
p[i] = pNext[i];
223226
endCapNorm[i] = sNext[i];
224227
}
228+
console.log(`Third Point: ${p}`);
229+
console.log(`endCapNorm: ${endCapNorm}`);
225230
vtkMath.normalize(endCapNorm);
231+
console.log(`endCapNorm: ${endCapNorm}`);
226232
} else {
227233
for (let i = 0; i < 3; ++i) {
228234
p[i] = pNext[i];
229235
}
236+
console.log(`Second Point: ${p}`);
230237
pNext = inPts.slice(3 * pts[j + 1], 3 * (pts[j + 1] + 1));
231238
for (let i = 0; i < 3; ++i) {
232239
sPrev[i] = sNext[i];
233240
sNext[i] = pNext[i] - p[i];
234241
}
235242
}
236243

237-
if (vtkMath.norm(sNext) === 0.0) {
244+
if (vtkMath.normalize(sNext) === 0.0) {
238245
vtkWarningMacro('Coincident points!');
239246
return 0;
240247
}
241248

242249
for (let i = 0; i < 3; ++i) {
243250
s[i] = (sPrev[i] + sNext[i]) / 2.0; // average vector
244251
}
252+
console.log(`Average Vector: ${s}`);
245253

246254
n = inNormals.slice(3 * pts[j], 3 * (pts[j] + 1));
255+
console.log(`Normal for point ${pts[j]}: ${n}`);
247256
// if s is zero then just use sPrev cross n
248-
if (vtkMath.norm(s) === 0.0) {
257+
if (vtkMath.normalize(s) === 0.0) {
249258
vtkMath.cross(sPrev, n, s);
250-
if (vtkMath.norm(s) === 0.0) {
259+
if (vtkMath.normalize(s) === 0.0) {
251260
vtkDebugMacro('Using alternate bevel vector');
252261
}
253262
}
254263

255264
vtkMath.cross(s, n, w);
256-
if (vtkMath.norm(w) === 0.0) {
265+
if (vtkMath.normalize(w) === 0.0) {
257266
let msg = 'Bad normal: s = ';
258267
msg += `${s[0]}, ${s[1]}, ${s[2]}`;
259268
msg += ` n = ${n[0]}, ${n[1]}, ${n[2]}`;
@@ -284,12 +293,14 @@ function vtkTubeFilter(publicAPI, model) {
284293
// create points around line
285294
if (model.sidesShareVertices) {
286295
for (let k = 0; k < model.numberOfSides; ++k) {
296+
console.log(`Point ${ptId}`);
287297
for (let i = 0; i < 3; ++i) {
288298
normal[i] = (w[i] * Math.cos(k * theta)) + (nP[i] * Math.sin(k * theta));
289299
s[i] = p[i] + (model.radius * sFactor * normal[i]);
290300
newPts[(3 * ptId) + i] = s[i];
291301
newNormals[(3 * ptId) + i] = normal[i];
292302
}
303+
console.log(`${s}`);
293304
outPD.passData(pd, pts[j], ptId);
294305
ptId++;
295306
} // for each side
@@ -319,6 +330,7 @@ function vtkTubeFilter(publicAPI, model) {
319330
} // else separate vertices
320331
} // for all points in the polyline
321332

333+
console.log(`NewNormals size: ${newNormals.length}`);
322334
// Produce end points for cap. They are placed at tail end of points.
323335
if (model.capping) {
324336
let numCapSides = model.numberOfSides;
@@ -335,9 +347,11 @@ function vtkTubeFilter(publicAPI, model) {
335347
newPts[(3 * ptId) + i] = s[i];
336348
newNormals[(3 * ptId) + i] = startCapNorm[i];
337349
}
350+
console.log(`Start cap Id: ${ptId} : ${startCapNorm}`);
338351
outPD.passData(pd, pts[0], ptId);
339352
ptId++;
340353
}
354+
console.log(`NewNormals size: ${newNormals.length}`);
341355

342356
// the end cap
343357
let endOffset = offset + ((npts - 1) * model.numberOfSides);
@@ -350,6 +364,7 @@ function vtkTubeFilter(publicAPI, model) {
350364
newPts[(3 * ptId) + i] = s[i];
351365
newNormals[(3 * ptId) + i] = endCapNorm[i];
352366
}
367+
console.log(`end cap Id: ${ptId} : ${endCapNorm}`);
353368
outPD.passData(pd, pts[npts - 1], ptId);
354369
ptId++;
355370
}
@@ -372,7 +387,7 @@ function vtkTubeFilter(publicAPI, model) {
372387
for (let k = offset; k < (model.numberOfSides + offset); k += model.onRatio) {
373388
i1 = k % model.numberOfSides;
374389
i2 = (k + 1) % model.numberOfSides;
375-
outCellId = newStrips.getNumberOfCells();
390+
outCellId = newStrips.getNumberOfCells(true);
376391
newStripsData[newStripsData.length] = (npts * 2);
377392
for (let i = 0; i < npts; ++i) {
378393
i3 = i * model.numberOfSides;
@@ -385,7 +400,7 @@ function vtkTubeFilter(publicAPI, model) {
385400
for (let k = offset; k < (model.numberOfSides + offset); k += model.onRatio) {
386401
i1 = (2 * (k % model.numberOfSides)) + 1;
387402
i2 = 2 * ((k + 1) % model.numberOfSides);
388-
outCellId = newStrips.getNumberOfCells();
403+
outCellId = newStrips.getNumberOfCells(true);
389404
newStripsData[newStripsData.length] = (npts * 2);
390405
for (let i = 0; i < npts; ++i) {
391406
i3 = i * 2 * model.numberOfSides;
@@ -407,7 +422,7 @@ function vtkTubeFilter(publicAPI, model) {
407422
}
408423

409424
// The start cap
410-
outCellId = newStrips.getNumberOfCells();
425+
outCellId = newStrips.getNumberOfCells(true);
411426
newStripsData[newStripsData.length] = model.numberOfSides;
412427
newStripsData[newStripsData.length] = startIdx;
413428
newStripsData[newStripsData.length] = startIdx + 1;
@@ -427,7 +442,7 @@ function vtkTubeFilter(publicAPI, model) {
427442

428443
// The end cap - reversed order to be consistent with normal
429444
startIdx += model.numberOfSides;
430-
outCellId = newStrips.getNumberOfCells();
445+
outCellId = newStrips.getNumberOfCells(true);
431446
newStripsData[newStripsData.length] = model.numberOfSides;
432447
newStripsData[newStripsData.length] = startIdx;
433448
newStripsData[newStripsData.length] = startIdx + model.numberOfSides - 1;
@@ -446,6 +461,93 @@ function vtkTubeFilter(publicAPI, model) {
446461
}
447462
}
448463

464+
function generateTCoords(offset, npts, pts, inPts, inScalars, newTCoords) {
465+
let numSides = model.numberOfSides;
466+
if (!model.sidesShareVertices) {
467+
numSides = 2 * model.numberOfSides;
468+
}
469+
470+
let tc = 0.0;
471+
let s0 = 0.0;
472+
let s = 0.0;
473+
const inScalarsData = inScalars.getData();
474+
if (model.generateTCoords === VtkTCoords.TCOORDS_FROM_SCALARS) {
475+
s0 = inScalarsData[pts[0]];
476+
for (let i = 0; i < npts; ++i) {
477+
s = inScalarsData[pts[i]];
478+
tc = (s - s0) / model.textureLength;
479+
for (let k = 0; k < numSides; ++k) {
480+
const tcy = k / (numSides - 1);
481+
const tcId = 2 * (offset + (i * numSides) + k);
482+
newTCoords[tcId] = tc;
483+
newTCoords[tcId + 1] = tcy;
484+
}
485+
}
486+
} else if (model.generateTCoords === VtkTCoords.TCOORDS_FROM_LENGTH) {
487+
let len = 0.0;
488+
const xPrev = inPts.slice(3 * pts[0], 3 * (pts[0] + 1));
489+
for (let i = 0; i < npts; ++i) {
490+
const x = inPts.slice(3 * pts[i], 3 * (pts[i] + 1));
491+
len += Math.sqrt(vtkMath.distance2BetweenPoints(x, xPrev));
492+
tc = len / model.textureLength;
493+
for (let k = 0; k < numSides; ++k) {
494+
const tcy = k / (numSides - 1);
495+
const tcId = 2 * (offset + (i * numSides) + k);
496+
newTCoords[tcId] = tc;
497+
newTCoords[tcId + 1] = tcy;
498+
}
499+
for (let k = 0; k < 3; ++k) {
500+
xPrev[k] = x[k];
501+
}
502+
}
503+
} else if (model.generateTCoords === VtkTCoords.TCOORDS_FROM_NORMALIZED_LENGTH) {
504+
let len = 0.0;
505+
let len1 = 0.0;
506+
let xPrev = inPts.slice(3 * pts[0], 3 * (pts[0] + 1));
507+
for (let i = 0; i < npts; ++i) {
508+
const x = inPts.slice(3 * pts[i], 3 * (pts[i] + 1));
509+
len1 += Math.sqrt(vtkMath.distance2BetweenPoints(x, xPrev));
510+
for (let k = 0; k < 3; ++k) {
511+
xPrev[k] = x[k];
512+
}
513+
}
514+
xPrev = inPts.slice(3 * pts[0], 3 * (pts[0] + 1));
515+
for (let i = 0; i < npts; ++i) {
516+
const x = inPts.slice(3 * pts[i], 3 * (pts[i] + 1));
517+
len += Math.sqrt(vtkMath.distance2BetweenPoints(x, xPrev));
518+
tc = len / len1;
519+
for (let k = 0; k < numSides; ++k) {
520+
const tcy = k / (numSides - 1);
521+
const tcId = 2 * (offset + (i * numSides) + k);
522+
newTCoords[tcId] = tc;
523+
newTCoords[tcId + 1] = tcy;
524+
}
525+
for (let k = 0; k < 3; ++k) {
526+
xPrev[k] = x[k];
527+
}
528+
}
529+
}
530+
531+
// Capping, set the endpoints as appropriate
532+
if (model.capping) {
533+
const startIdx = offset + (npts * numSides);
534+
535+
// start cap
536+
for (let ik = 0; ik < model.numberOfSides; ++ik) {
537+
const tcId = 2 * (startIdx + ik);
538+
newTCoords[tcId] = 0.0;
539+
newTCoords[tcId + 1] = 0.0;
540+
}
541+
542+
// end cap
543+
for (let ik = 0; ik < model.numberOfSides; ++ik) {
544+
const tcId = 2 * (startIdx + model.numberOfSides + ik);
545+
newTCoords[tcId] = 0.0;
546+
newTCoords[tcId + 1] = 0.0;
547+
}
548+
}
549+
}
550+
449551
publicAPI.requestData = (inData, outData) => { // implement requestData
450552
// pass through for now
451553
outData[0] = inData[0];
@@ -486,7 +588,11 @@ function vtkTubeFilter(publicAPI, model) {
486588
const newPts = vtkPoints.newInstance(
487589
{ dataType: pointType, size: (numNewPts * 3), numberOfComponents: 3 });
488590
// const newPtsData = newPts.getData();
489-
const newNormalsData = new Float32Array(3 * numNewPts);
591+
let numNormals = 3 * numNewPts;
592+
if (model.capping) {
593+
numNormals = 3 * (numNewPts + (2 * model.numberOfSides));
594+
}
595+
const newNormalsData = new Float32Array(numNormals);
490596
const newNormals = vtkDataArray.newInstance(
491597
{ numberOfComponents: 3, values: newNormalsData, name: 'TubeNormals' });
492598
const newStripsData = new Uint32Array(0);
@@ -534,7 +640,18 @@ function vtkTubeFilter(publicAPI, model) {
534640

535641
const outPD = output.getPointData();
536642
outPD.copyNormalsOff();
643+
537644
// TCoords
645+
let newTCoords = null;
646+
if ((model.generateTCoords === VtkTCoords.TCOORDS_FROM_SCALARS && inScalars) ||
647+
model.generateTCoords === VtkTCoords.TCOORDS_FROM_LENGTH ||
648+
model.generateTCoords === VtkTCoords.TCOORDS_FROM_NORMALIZED_LENGTH) {
649+
const newTCoordsData = new Float32Array(2 * numNewPts);
650+
newTCoords = vtkDataArray.newInstance(
651+
{ numberOfComponents: 2, values: newTCoordsData, name: 'TCoords' });
652+
outPD.copyTCoordsOff();
653+
}
654+
538655
outPD.passData(input.getPointData());
539656

540657
// Create points along each polyline that are connected into numberOfSides
@@ -562,6 +679,9 @@ function vtkTubeFilter(publicAPI, model) {
562679
// generate strips for the polyline
563680
generateStrips(offset, npts, inCellId, input.getCellData(), outCD, newStrips);
564681
// generate texture coordinates for the polyline
682+
if (newTCoords) {
683+
generateTCoords(offset, npts, pts, inPts.getData(), inScalars, newTCoords.getData());
684+
}
565685
} else {
566686
// skip tubing this line
567687
vtkWarningMacro('Could not generate points');
@@ -574,7 +694,10 @@ function vtkTubeFilter(publicAPI, model) {
574694

575695
output.setPoints(newPts);
576696
output.setStrips(newStrips);
697+
output.setPointData(outPD);
577698
outPD.setNormals(newNormals);
699+
const s = JSON.stringify(output.getState());
700+
console.log(`${s}`);
578701
outData[0] = output;
579702
};
580703
}
@@ -595,6 +718,8 @@ const DEFAULT_VALUES = {
595718
capping: false,
596719
onRatio: 1,
597720
offset: 0,
721+
generateTCoords: VtkTCoords.TCOORDS_OFF,
722+
textureLength: 1.0,
598723
};
599724

600725
// ----------------------------------------------------------------------------
@@ -616,6 +741,8 @@ export function extend(publicAPI, model, initialValues = {}) {
616741
'capping',
617742
'onRatio',
618743
'offset',
744+
'generateTCoords',
745+
'textureLength',
619746
]);
620747

621748
// Make this a VTK object

0 commit comments

Comments
 (0)