about LineWidget?

How to handle the situation where the mouse and handle positions do not coincide when using LineWidget for distance measurement on MPR images?

the code

scene.widgetRender = vtkRenderer.newInstance();
  scene.widgetRender.setLayer(1);
  scene.renderWindow.setNumberOfLayers(2);
  scene.widgetRender.setInteractive(false);
  scene.renderWindow.addRenderer(scene.widgetRender);

  const update = () => {
    const baseCamera = scene.renderer.getActiveCamera();
    scene.widgetRender.getActiveCamera().set({
      viewUp: baseCamera.getViewUp(),
      position: baseCamera.getPosition(),
      viewAngle: baseCamera.getViewAngle(),
      focalPoint: baseCamera.getFocalPoint()
    });
  };
  update();

  scene.widgetManager = vtkWidgetManager.newInstance(ViewTypes.SLICE);
  scene.widgetManager.disablePicking();
  scene.widgetManager.setRenderer(scene.renderer);
  scene.widgetManager.setRenderer(scene.widgetRender);

  scene.widgets = {};
  scene.handles = {};

  scene.widgets.lineWidget = vtkLineWidget.newInstance();
  scene.handles.lineHandle = scene.widgetManager.addWidget(scene.widgets.lineWidget, ViewTypes.SLICE);
  scene.handles.lineHandle.onInteractionEvent(() => {
    const text = scene.widgets.lineWidget.getDistance().toFixed(2) + 'mm';
    scene.widgets.lineWidget.getWidgetState().getText().setText(text);
  });

  setupSVG(scene.widgetRender, scene.widgets.lineWidget);

Could it be linked to the window.devicePixelRatio value ?

Hello, Julien Finet, thank you for your response to this question.

The window.devicePixelRatio value is 1.25.

I implemented MPR using volume, volumMapper, GenericRenderWindow, and vtkInteractorStyleMPRSlice.
There is a renderer in GenericRenderWindow, but when I was using LineWidget for measurement, I recreated a renderer,
As with my code above, I’m not sure if this is the right thing to do. Currently, both the measurement line and value have been displayed, only the handle and mouse positions are not aligned.

Try with a window.devicePixelRatio of 1 (you can do it by changing your Operating System zoom ratio) and see if you still have the problem.
If the problem disapears with devicePixelRatio, it means it is the problem you are facing.
Check in VTK.js code where window.devicePixelRatio is being used to see what you need to change.

1 Like

I tried setting window.devicePixelRatio to 1, but the problem still persists.
In the above code, modify the code as follows:
scene.widgetRender.setInteractive(true);
The handle successfully overlaps with the mouse, but the length measured by LineWidget is incorrect.
So, is it related to certain location attributes, but I don’t know how to do it.

In MPR, measurements need to be performed on each slice, so that after switching slices, the measurement data will be hidden or displayed. Do I need to use components like PlaneManipulator, and is it helpful for the problem?

PlaneManipulator is used by the widget when “interacting” with the handles. It is not used when scrolling. If you want to the measure to “stick” to the current slice, you need to manually project it on the current slice whenever you are notified that the slice change. You can use the PlaneManipulator to do the projection of the handles if you want.

Thank you for your reply.
You’re right, I’ve found the reason.
Because I was working on LineWidget, I recreated the renderer
Currently, we have changed to using the same renderer, but a new issue has arisen where the drawn lines and handles are obstructed by the image. What should I do?
Behind

Looking forward to your answer! thank you!

Are you sure the widgets are in the same plane as the image slice ?
if so, you might want to consider resolveCoincidentTopology

1 Like

Sorry, I don’t know how to use ‘resolveCoincidentTopology’ Can you provide more hints.

I will add vtkImageData to the volume and add the volume to scene.renderer.
Then
scene. widgetManager. setRenderer (scene. renderer);
… Initialize widgets, such as LineWidget, RectangleWidget, etc

I use the following code to ensure that they are on the same plane, for example:
const camera = scene.renderer.getActiveCamera();
const manipulator = scene.widgets.rectangleWidget.getManipulator();
manipulator.setUserOrigin(…camera.getFocalPoint());
manipulator.setUserNormal(…camera.getDirectionOfProjection());

I don’t know if this is enough

Ideally you should not use the camera information to specify the manipulator but more the widget internal state, e.g. rcw.getWidgetState().getPlanes()[0].origin|normal (or something like that).

Widget representations have a coincidentTopologyParameters parameter. You may want to play with the Line offset + factor

I am trying to find
The getPlans method under scene. widgets. rectangWidget. getWidgetState() does not have this method.
After setting the coincideTologyParameters parameter, there was no change, and I am still searching.
From the measured image after the operation, the line is obscured by the image. This should be the main issue(coincidentTopologyParameters).
Behind

thank you!

getPlanes is in the widget state of the reslice cursor widget.
Another approach is to set 'relative’coincidentTopology polygon parameter on the image mapper, so that it “shift away from the camera” the rendering of the image.

I did not use resciceCursorWidget.
I am using volumMapper and trying to set the ‘relative’ coincideTopology parameter as you suggested.
thank you

Thanks for your help.

After loading the MPR using volume and volumMapper, I referred to the demo and have implemented and displayed lines for circles, ellipses, and rectangles.

But when using its LineWidget component to measure straight lines, the lines still cannot be displayed, and I have no other way. It’s really difficult for me, a beginner, to adjust the thickness and color of the lines to match the circular, elliptical, and rectangular styles.

Are the usage methods of LineWidget and PaintWidget different?
What should I do? I’m very distressed!

What happens if you slightly move towards the camera the line widget handles coordinates ?

I don’t quite understand what you mean.
I tried to check two values related to the coordinates you mentioned when LlineWidget handles were moved
getCoordinateSystem = 1
getCoincidentTopologyParameters = [
{
Line: { factor: -1, offset: -1 },
Point: { factor: -1, offset: -1 },
Ploygon: { factor: -3, offset: -3 }
},
{
Line: { factor: -1, offset: -1 },
Point: { factor: -1, offset: -1 },
Ploygon: { factor: -3, offset: -3 }
},
{
Line: { factor: -1, offset: -1 },
Point: { factor: -1, offset: -1 },
Ploygon: { factor: -3, offset: -3 }
},
{
Line: { factor: -1, offset: -1 },
Point: { factor: -1, offset: -1 },
Ploygon: { factor: -3, offset: -3 }
}
]

First you need to ensure your line widget handles are correctly placed (i.e. in the same plane than your slice). You can check it with: o1 = widget.getWidgetState().getHandle1().getOrigin().

This is because I doubt that relying on camera.getFocalPoint in manipulator.setUserOrigin(…camera.getFocalPoint()); is what you really want

Then you slightly modify their origin to see if that makes them show up:
widget.getWidgetState().getHandle1().setOrigin(o1[0] - cd[0], o1[1] - cd[1], o1[2] - cd[2]) where cd is the camera direction.

1 Like

You’re absolutely right!
I checked that the value of widget. getWidgetState(). getHandle1(). getOrigin() is null.
Because I haven’t set any values related to the handle.

I don’t want to trouble you to read too much of my code, which would waste your time, but I’m afraid I won’t be able to explain it clearly. Please allow me to demonstrate to you in order to further address the issue.

The first step is to assign the camera attribute from the original rendered image renderer to the camera of the newly created renderer. The code is as follows:

const updateCamera = ()=>{
   const baseCamera = this.renderer.getActiveCamera();
   const paintCamera = this.paintRenderer.getActiveCamera();

   paintCamera.set({
       viewUp: baseCamera.getViewUp(),
       position: baseCamera.getPosition(),
       viewAngle: baseCamera.getViewAngle(),
       focalPoint: baseCamera.getFocalPoint(),
       windowCenter: baseCamera.getWindowCenter(),
       parallelScale: baseCamera.getParallelScale(),
       parallelProjection: baseCamera.getParallelProjection()
   });

   paintCamera.setPhysicalScale(baseCamera.getPhysicalScale());
   paintCamera.setClippingRange(...baseCamera.getClippingRange());
   paintCamera.setFreezeFocalPoint(baseCamera.getFreezeFocalPoint());    
paintCamera.setPhysicalTranslation(baseCamera.getPhysicalTranslation());
}
this.renderWindow.getInteractor().onAnimation(updateCameras);
updateCameras()

The second step is to assign two camera attributes from the original rendered image renderer to the manipulator. The code is as follows:

const camera = this.renderer.getActiveCamera();
const focalPoint = camera.getFocalPoint();
const direction = camera.getDirectionOfProjection();
Object.keys(this.widgets).forEach((key, index) => {
     this.widgets[key].getManipulator().setUserNormal(...direction);
     this.widgets[key].getManipulator().setUserOrigin(...focalPoint);
});

So, I am setting up
When widget. getWidgetState(). getHandle1(). setOrigin (o1 [0] - cd [0], o1 [1] - cd [1], o1 [2] - cd [2]),
What can be obtained is the value of direction, which is the getDirectionOfProjection value in the camera.
But if the value of o1 is null, the values of o1 [0], o1 [1], and o1 [0] cannot be calculated. How should I handle it?

look forward to your reply!!!!

You should not use paintCamera.set(), this is an internal function that may not update the camera internal state correctly. I guess a copyCamera() convenient function could be added in vtkCamera (please contribute such PR).

The lineWidget handle1 origin is null before the line widget is “placed”. I meant to add the offset AFTER the line widget is placed by the user.

1 Like