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'))