Skip to content

Commit 87a3724

Browse files
bruyeretfinetjul
authored andcommitted
docs(cpr): Add a stretched mode + spine centerline
1 parent 86d0b0d commit 87a3724

File tree

4 files changed

+101
-43
lines changed

4 files changed

+101
-43
lines changed

Sources/Rendering/Core/ImageCPRMapper/example/controller.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
<table>
2+
<tr>
3+
<td>
4+
<select id='mode' style="width: 100%"></select>
5+
</td>
6+
</tr>
27
<tr>
38
<td>
49
<select id='centerline' style="width: 100%"></select>

Sources/Rendering/Core/ImageCPRMapper/example/index.js

Lines changed: 95 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ import vtkWidgetManager from '@kitware/vtk.js/Widgets/Core/WidgetManager';
2727
import widgetBehavior from 'vtk.js/Sources/Widgets/Widgets3D/ResliceCursorWidget/cprBehavior';
2828

2929
import controlPanel from './controller.html';
30-
import centerlineJSON from './centerline.json';
30+
import aortaJSON from './aorta_centerline.json';
31+
import spineJSON from './spine_centerline.json';
3132

3233
const volumePath = `${__BASE_PATH__}/data/volume/LIDC2.vti`;
33-
const centerlineJsons = { 'Base centerline': centerlineJSON };
34+
const centerlineJsons = { Aorta: aortaJSON, Spine: spineJSON };
3435
const centerlineKeys = Object.keys(centerlineJsons);
3536

3637
// ----------------------------------------------------------------------------
@@ -44,6 +45,7 @@ const renderWindow = fullScreenRenderer.getRenderWindow();
4445
fullScreenRenderer.addController(controlPanel);
4546
const angleEl = document.getElementById('angle');
4647
const centerlineEl = document.getElementById('centerline');
48+
const modeEl = document.getElementById('mode');
4749

4850
const interactor = renderWindow.getInteractor();
4951
interactor.setInteractorStyle(vtkInteractorStyleImage.newInstance());
@@ -132,10 +134,50 @@ function updateDistanceAndDirection() {
132134
const worldTangent = vec3.cross([], worldBitangent, worldNormal);
133135
vec3.normalize(worldTangent, worldTangent);
134136
const worldWidgetCenter = widgetState.getCenter();
137+
const distance = cprManipulator.getCurrentDistance();
138+
139+
// CPR mapper tangent and bitangent directions update
140+
const { orientation } = mapper.getCenterlinePositionAndOrientation(distance);
141+
// modelDirections * baseDirections = worldDirections
142+
// => baseDirections = modelDirections^(-1) * worldDirections
143+
const modelDirections = mat3.fromQuat([], orientation);
144+
const inverseModelDirections = mat3.invert([], modelDirections);
145+
const worldDirections = mat3.fromValues(
146+
...worldTangent,
147+
...worldBitangent,
148+
...worldNormal
149+
);
150+
const baseDirections = mat3.mul([], inverseModelDirections, worldDirections);
151+
mapper.setDirectionMatrix(baseDirections);
152+
153+
// Cross renderer update
154+
widget.updateReslicePlane(reslice, crossViewType);
155+
resliceActor.setUserMatrix(reslice.getResliceAxes());
156+
widget.updateCameraPoints(crossRenderer, crossViewType, false, true, false);
157+
const crossCamera = crossRenderer.getActiveCamera();
158+
crossCamera.setViewUp(
159+
modelDirections[3],
160+
modelDirections[4],
161+
modelDirections[5]
162+
);
163+
164+
// Update plane manipulator origin / normal for the cross view
165+
planeManipulator.setUserOrigin(worldWidgetCenter);
166+
planeManipulator.setUserNormal(worldNormal);
167+
168+
// Find the angle
169+
const signedRadAngle = Math.atan2(baseDirections[1], baseDirections[0]);
170+
const signedDegAngle = (signedRadAngle * 180) / Math.PI;
171+
const degAngle = signedDegAngle > 0 ? signedDegAngle : 360 + signedDegAngle;
172+
angleEl.value = degAngle;
173+
updateState(
174+
widgetState,
175+
widget.getScaleInPixels(),
176+
widget.getRotationHandlePosition()
177+
);
135178

136179
const width = mapper.getWidth();
137180
const height = mapper.getHeight();
138-
const distance = cprManipulator.getCurrentDistance();
139181

140182
// CPR actor matrix update
141183
const worldActorTranslation = vec3.scaleAndAdd(
@@ -187,45 +229,6 @@ function updateDistanceAndDirection() {
187229
stretchRenderer.resetCameraClippingRange();
188230
interactor.render();
189231

190-
// CPR mapper tangent and bitangent directions update
191-
const { orientation } = mapper.getCenterlinePositionAndOrientation(distance);
192-
// modelDirections * baseDirections = worldDirections
193-
// => baseDirections = modelDirections^(-1) * worldDirections
194-
const modelDirections = mat3.fromQuat([], orientation);
195-
const inverseModelDirections = mat3.invert([], modelDirections);
196-
const worldDirections = mat3.fromValues(
197-
...worldTangent,
198-
...worldBitangent,
199-
...worldNormal
200-
);
201-
const baseDirections = mat3.mul([], inverseModelDirections, worldDirections);
202-
mapper.setDirectionMatrix(baseDirections);
203-
204-
// Cross renderer update
205-
widget.updateReslicePlane(reslice, crossViewType);
206-
resliceActor.setUserMatrix(reslice.getResliceAxes());
207-
widget.updateCameraPoints(crossRenderer, crossViewType, false, true, false);
208-
const crossCamera = crossRenderer.getActiveCamera();
209-
crossCamera.setViewUp(
210-
modelDirections[3],
211-
modelDirections[4],
212-
modelDirections[5]
213-
);
214-
215-
// Update plane manipulator origin / normal for the cross view
216-
planeManipulator.setUserOrigin(worldWidgetCenter);
217-
planeManipulator.setUserNormal(worldNormal);
218-
219-
// Find the angle
220-
const signedRadAngle = Math.atan2(baseDirections[1], baseDirections[0]);
221-
const signedDegAngle = (signedRadAngle * 180) / Math.PI;
222-
const degAngle = signedDegAngle > 0 ? signedDegAngle : 360 + signedDegAngle;
223-
angleEl.value = degAngle;
224-
updateState(
225-
widgetState,
226-
widget.getScaleInPixels(),
227-
widget.getRotationHandlePosition()
228-
);
229232
renderWindow.render();
230233
}
231234

@@ -262,7 +265,8 @@ function setCenterlineKey(centerlineKey) {
262265
centerline.modified();
263266

264267
const midPointDistance = mapper.getHeight() / 2;
265-
cprManipulator.setCurrentDistance(midPointDistance);
268+
const { worldCoords } = cprManipulator.distanceEvent(midPointDistance);
269+
widgetState.setCenter(worldCoords);
266270
updateDistanceAndDirection();
267271

268272
widgetState[`getAxis${crossPlane}in${stretchPlane}`]().setManipulator(
@@ -350,6 +354,54 @@ angleEl.addEventListener('input', () =>
350354
setAngleFromSlider(radiansFromDegrees(Number.parseFloat(angleEl.value, 10)))
351355
);
352356

357+
function useStraightenedMode() {
358+
mapper.setCenterPoint(null);
359+
mapper.setUseUniformOrientation(false);
360+
mapper.getOrientedCenterline().setDistanceFunction(vec3.dist);
361+
updateDistanceAndDirection();
362+
}
363+
364+
function useStretchedMode() {
365+
mapper.setCenterPoint(centerline.getPoints().getPoint(0));
366+
mapper.setUseUniformOrientation(true);
367+
mapper.getOrientedCenterline().setDistanceFunction((a, b) => {
368+
const worldTangent = vec3.transformQuat(
369+
[],
370+
mapper.getTangentDirection(),
371+
mapper.getUniformOrientation()
372+
);
373+
const vec = vec3.subtract([], a, b);
374+
const d2 = vec3.squaredLength(vec);
375+
const x = vec3.dot(worldTangent, vec);
376+
return Math.sqrt(d2 - x * x);
377+
});
378+
updateDistanceAndDirection();
379+
}
380+
381+
let cprMode;
382+
function setUseStretched(value) {
383+
cprMode = value;
384+
switch (cprMode) {
385+
case 'stretched':
386+
useStretchedMode();
387+
break;
388+
default:
389+
useStraightenedMode();
390+
break;
391+
}
392+
}
393+
394+
const stretchEl = document.createElement('option');
395+
stretchEl.innerText = 'Stretched Mode';
396+
stretchEl.value = 'stretched';
397+
modeEl.appendChild(stretchEl);
398+
const straightEl = document.createElement('option');
399+
straightEl.innerText = 'Straightened Mode';
400+
straightEl.value = 'straightened';
401+
modeEl.appendChild(straightEl);
402+
modeEl.addEventListener('input', () => setUseStretched(modeEl.value));
403+
modeEl.value = 'straightened';
404+
353405
stretchViewWidgetInstance.onInteractionEvent(updateDistanceAndDirection);
354406
crossViewWidgetInstance.onInteractionEvent(updateDistanceAndDirection);
355407

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"position":[187.7506765569237,170.44962465701968,330.00000000000006,191.0904431262933,159.31706942578768,297.50000000000006,189.4205598416085,146.5146309098709,267.50000000000006,180.5145156566229,133.7121923939541,240.00000000000006,177.7313768488149,124.80614820896851,217.50000000000006,173.83498251788373,122.57963716272208,207.50000000000006,172.16509923319893,118.68324283179088,195.00000000000006,167.71207714070613,113.67359297773648,172.50000000000006,166.04219385602133,109.77719864680527,150.00000000000006,166.59882161758293,109.77719864680527,132.50000000000006,166.04219385602133,112.00370969305168,112.50000000000006,166.59882161758293,119.79649835491408,90.00000000000006,167.71207714070613,128.14591477833807,72.50000000000006,166.04219385602133,139.83509777113167,57.50000000000006,163.81568280977493,151.52428076392528,42.50000000000006,161.03254400196693,165.99660256452688,27.500000000000057,159.36266071728213,177.68578555732046,15.000000000000057,160.4759162404053,183.8086909344981,2.5000000000000564],"orientation":[1.0,0.0,0.0,0,0.0,-0.941598797565795,0.32253540367885714,0,0.09676062110365714,-0.32253540367885714,-0.941598797565795,0,0,0,0,1,1.0,0.0,0.0,0,0.0,-0.9335720675686494,0.35752066559629414,0,0.02494330225090427,-0.35752066559629414,-0.9335720675686494,0,0,0,0,1,1.0,0.0,0.0,0,0.0,-0.9008921273230787,0.4011692545922503,0,-0.16570034428810357,-0.4011692545922503,-0.9008921273230787,0,0,0,0,1,1.0,0.0,0.0,0,0.0,-0.8968851226076998,0.3894003033765195,0,-0.20967708643351063,-0.3894003033765195,-0.8968851226076998,0,0,0,0,1,1.0,0.0,0.0,0,0.0,-0.9286472589653068,0.3180989815619539,0,-0.19085938893717125,-0.3180989815619539,-0.9286472589653068,0,0,0,0,1,1.0,0.0,0.0,0,0.0,-0.9385406161572778,0.25540423934973344,0,-0.23218567213611907,-0.25540423934973344,-0.9385406161572778,0,0,0,0,1,1.0,0.0,0.0,0,0.0,-0.9554829059915173,0.24313065653596855,0,-0.16715232636847838,-0.24313065653596855,-0.9554829059915173,0,0,0,0,1,1.0,0.0,0.0,0,0.0,-0.9723492573007179,0.19243967663906952,0,-0.13230227768936031,-0.19243967663906952,-0.9723492573007179,0,0,0,0,1,1.0,0.0,0.0,0,0.0,-0.9949075323347023,0.09691380171974212,0,-0.027689657634212037,-0.09691380171974212,-0.9949075323347023,0,0,0,0,1,1.0,0.0,0.0,0,0.0,-0.9982420327181917,-0.05926925100465373,0,0.0,0.05926925100465373,-0.9982420327181917,0,0,0,0,1,1.0,0.0,0.0,0,0.0,-0.9733185352162832,-0.22945812037151517,0,0.0,0.22945812037151517,-0.9733185352162832,0,0,0,0,1,1.0,0.0,0.0,0,0.0,-0.9266413781634073,-0.3739508791706536,0,0.03868457370730903,0.3739508791706536,-0.9266413781634073,0,0,0,0,1,1.0,0.0,0.0,0,0.0,-0.8511163169270675,-0.5247747363540923,0,-0.01457707600983591,0.5247747363540923,-0.8511163169270675,0,0,0,0,1,1.0,0.0,0.0,0,0.0,-0.7846711616302167,-0.6114776531642354,0,-0.10191294219403924,0.6114776531642354,-0.7846711616302167,0,0,0,0,1,1.0,0.0,0.0,0,0.0,-0.7477784430289229,-0.6521003107232924,0,-0.12487027226616237,0.6521003107232924,-0.7477784430289229,0,0,0,0,1,1.0,0.0,0.0,0,0.0,-0.7195837665033622,-0.6845597875137116,0,-0.11652081489595105,0.6845597875137116,-0.7195837665033622,0,0,0,0,1,1.0,0.0,0.0,0,0.0,-0.8142932282848118,-0.5801705176511281,0,-0.01813032867659868,0.5801705176511281,-0.8142932282848118,0,0,0,0,1,1.0,0.0,0.0,0,0.0,-0.8951910891649871,-0.43849362667598396,0,0.07972611394108559,0.43849362667598396,-0.8951910891649871,0,0,0,0,1]}

0 commit comments

Comments
 (0)