Click-and-Drag Translation with VTKRenderer and Server-side Rendering

Hello,

We are working on building a web-based viewer using the Paraview server with server-side rendering (VTKRenderer, simple, wslink) and we have loaded both PNGs series data and OBJs. We’re looking to implement object translation from browser interaction. Currently we’re using the Transform filter to translate the location of the OBJs which looks like it removes the previous OBJ from the view then renders the translated OBJ, which functions.

However, part of our use case is that we need to be able to translate objects in the browser in real-time. Is there any other VTK/Paraview/KitWare feature or plugin or software that does support translation by click-and-drag? How could we go about implementing that beyond passing mouse input changes to the Transform filters’ translate?

Best, GM

ParaViewGlance is something that looks a little bit like that. You can drag and drop file into it.
https://kitware.github.io/paraview-glance/app/

I’m not sure this is what you are looking for. You may want to give us a precise user scenario.

Hey Mathieu,

Thanks for your reply, but that’s not quite what I mean.

I mean if I load two objects into the browser application (such as ParaView Glance) at once, I want to be able to select one of the objects and drag it away from origin and around the scene while the other object remains where it is. In WebGL context this is object translation.

In the Paraview App, it looks like this is the ‘Show Box’ functionality of the Transform filter. Is there any way to do this in the client-side web application?

Ok. I’ve moved your post to WebSupport.

You can use the ParaView widgets (i.e.: Box) to control filters with ParaViewWeb but it is not that straight forward to use them.

Here is a code snippet that should help you get 80% there.

from paraview import simple

# -----------------------------------------------------------------------------
# Helper methods
# -----------------------------------------------------------------------------
# Widget:
#  - Visibility
#  - Enabled
#
#  - MoveFacesEnabled
#  - RotationEnabled
#  - ScalingEnabled
#  - TranslationEnabled
#
#  - PlaceFactor
#  - PlaceWidget
#
#  - Position
#  - PositionInfo
#
#  - Rotation
#  - RotationInfo
#
#  - Scale  (or Length, pv 5.7+)
#  - ScaleInfo
# -----------------------------------------------------------------------------
# Box: ['Bounds', 'Input', 'Position', 'Rotation', 'Scale']
# -----------------------------------------------------------------------------

def _box_update_widget(self, widget):
    widget.Position = self.Position
    widget.Rotation = self.Rotation
    if not hasattr(self, 'Length'):
      widget.Scale = self.Scale
    else:
      widget.Scale = self.Length
    # update all the information-only properties together.
    widget.UpdatePropertyInformation()
    widget.UpdateVTKObjects()


def _box_widget_update(self, obj, event):
    self.GetProperty('Position').Copy(obj.GetProperty('PositionInfo'))
    self.GetProperty('Rotation').Copy(obj.GetProperty('RotationInfo'))
    if not hasattr(self, 'Length'):
      self.GetProperty('Scale').Copy(obj.GetProperty('ScaleInfo'))
    else:
      self.GetProperty('Length').Copy(obj.GetProperty('ScaleInfo'))
    self.UpdateVTKObjects()

def _expandBounds(bounds):
  size = [0.02*abs(bounds[1] - bounds[0]),
    0.02*abs(bounds[3] - bounds[2]),
    0.02*abs(bounds[5] - bounds[4])]
  return [bounds[0] - size[0], bounds[1] + size[0],
    bounds[2] - size[1], bounds[3] + size[1],
    bounds[4] - size[2], bounds[5] + size[2]]


# -----------------------------------------------------------------------------
# Public API
# -----------------------------------------------------------------------------

def updateWidgetStyle(wRep):
  # wRep.GetOutlineProperty().SetColor(...)
  # wRep.GetSelectedOutlineProperty().SetColor(...)
 
  # wRep.GetHandleProperty().SetColor(...)
  # wRep.GetFaceProperty().SetColor(...)
  # wRep.GetSelectedFaceProperty().SetColor(...)
  # wRep.GetSelectedHandleProperty().SetColor(...)
  pass


def setBoxWidgetBounds(sourceProxy, widgetProxy, dataBounds, options):
  widgetVTKRep = widgetProxy.GetRepresentationProxy().SMProxy.GetClientSideObject()
  dataBounds = _expandBounds(dataBounds)

  if hasattr(widgetProxy, 'UseReferenceBounds'):
    # PV 5.6+
    sourceProxy.UseReferenceBounds = 1
    sourceProxy.Bounds = dataBounds

    widgetProxy.UseReferenceBounds = 1
    widgetProxy.ReferenceBounds = dataBounds
    widgetProxy.UpdatePropertyInformation()
    widgetProxy.UpdateVTKObjects()
  else:
    # PV 5.6
    sourceProxy.Bounds = dataBounds
    widgetVTKRep.PlaceWidget(dataBounds)

  if options:
    if 'reset' in options and options['reset']:
      sourceProxy.Position = [0, 0, 0]
      sourceProxy.Rotation = [0, 0, 0]
      if not hasattr(sourceProxy, 'Length'):
        sourceProxy.Scale = [1, 1, 1]
      else:
        sourceProxy.Length = [1, 1, 1]
      sourceProxy.UpdateWidget(widgetProxy)

  sourceProxy.UpdateVTKObjects()


def createBoxWidget(sourceProxy, viewProxy, dataBounds):
  widgetProxy = simple.servermanager.rendering.BoxWidgetRepresentation(
    MoveFacesEnabled = 1,
    RotationEnabled = 1,
    ScalingEnabled = 0,
    TranslationEnabled = 1,
  )

  # Use custom colors for widget representation
  widgetVTKRep = widgetProxy.GetRepresentationProxy().SMProxy.GetClientSideObject()
  updateWidgetStyle(widgetVTKRep)

  viewProxy.Representations.append(widgetProxy)
  widgetProxy.Visibility = 1
  widgetProxy.Enabled = 1
  setBoxWidgetBounds(sourceProxy, widgetProxy, dataBounds, None)

  # Attach helper method to source proxy
  setattr(sourceProxy.__class__, 'UpdateWidget', _box_update_widget)
  setattr(sourceProxy.__class__, 'WidgetUpdate', _box_widget_update)

  widgetProxy.GetProperty('PlaceFactor').SetData(1.0)
  sourceProxy.UpdateWidget(widgetProxy)
  sourceProxy.Observed = widgetProxy
  sourceProxy.ObserverTag = widgetProxy.AddObserver("EndInteractionEvent", sourceProxy.WidgetUpdate)

  return widgetProxy

I forgot to mention that the source proxy for the widget is an implicit function like simple.servermanager._getPyProxy(pxm.NewProxy('implicit_functions', 'Box'))

Hi Jourdain,

Thanks so much for the snippet here on implementing the box widget. We have successfully ingrained what you’ve done here into our implementation and are able to create boxes and use them to rotate, translate, and scale the objects in the scene.

At this point, we are now struggling with deleting the boxes from the scene once we’re done with whatever manipulation was being attempting. We’ve attempted deleting the Box object itself via it’s internal __del__ function and are able to stop the box from continuing to affect the object, but that’s as close as we’ve gotten. We haven’t been able to remove a box from the scene. Also attempted the simple.Delete() and simple.Hide3DWidgets() functions and they do not appear to have any impact in scene (when not throwing errors).

How would you go about deleting a Box widget of the type implemented in your code snippet here from the scene?

Thanks and best wishes,
GM

Hello all,

We really appreciate your help here with implementing a Box transform widget and have successfully implemented and used it, but unfortunately we still have not made any progress on deleting a Box transform widget from a scene after manipulating an object, despite trying several different methods. What is the best way to remove a widget from a scene?

How would you recommend going about deleting a Box widget of the type implemented in the code snippet here?

Thank you,
GM