Scalar mapping of markup curve between points

I’m aiming to display a markup curve where the line between points is a unique solid color reflecting the distance between each control point, and which will update when points are modified.

My approach so far is to calculate the distance between each point and the previous, save this to an array (setting the first control point scalar value to 0), and add a static measurement to the markup curve. When a point is modified,I use RemoveAllMeasurements(), re-compute the distances, and re-add the updated measurements to the curve node.

adjacent_dist = slicer.vtkMRMLStaticMeasurement()
adjacent_dist.SetName(‘staticmeasure’)
adjacent_dist.SetUnits(‘’)
adjacent_dist.SetPrintFormat(“”)
adjacent_dist.SetControlPointValues(adjacent_distArray)
markupsNode.AddMeasurement(adjacent_dist)`

Since each control point is associated with only a single distance value (reflecting the length between it and the previous point), the color effect is a gradient change along the line between each point value, which is not desired in this case.

Is there a way to access how the scalars are mapped to the curve color (markupscurvedata array from slicer.util.arrayFromMarkupsCurveData())? Or is there a better approach?

Measurement values are always lienarly interpolated between control points. It would not be hard to add more interpolation options, but we would only implement this if there were at least a couple of use cases.

For now, I would recommend to create a slightly larger diameter tube model with the colors you desire. By adding an observer to the curve node, you can always keep your tube model up-to-date (following changes in the curve node).

For anyone interested in a similar effect I achieved the desired result with the code below and a polyline model. in this case I have the color of the connecting line change according to a threshold distance value of the connecting line

import numpy as np
#markupsNode = getNode('original_markup')
markupsNode = getNode('resampled')
slicer.mrmlScene.AddNewNodeByClass('vtkMRMLModelNode','newlinemodel')


def getdistancesbtwnpoints():
  numberfiducialpoints = markupsNode.GetNumberOfControlPoints()
  points = np.zeros([numberfiducialpoints,3])
  for k in range(0,numberfiducialpoints):
    markupsNode.GetNthControlPointPosition(k, points[k,:])
  # Calculate distances between adjacent points
  adjacent_distances = []
  for l in range(len(points) - 1):
    distance = np.linalg.norm(points[l + 1] - points[l])
    adjacent_distances.append(distance)
  # Convert the list of distances to a NumPy array
  adjacent_distances = np.array(adjacent_distances)
  # Print the distances
  #print(adjacent_distances)
  return adjacent_distances

adjdist = getdistancesbtwnpoints()

#####################create model########################

global polyline
polyline = vtk.vtkPolyData()

modelNode = getNode('newlinemodel')
modelNode.CreateDefaultDisplayNodes()
modelNode.SetAndObservePolyData(polyline)
modelNode.GetDisplayNode().SetSliceDisplayModeToProjection()
modelNode.GetDisplayNode().SetVisibility2D(1)
modelNode.GetDisplayNode().SetSliceIntersectionThickness(5)
# Set up coloring by selection array

def updatemodelcolor2(caller, event):
  print('Point locations')
  adjacent_dist = getdistancesbtwnpoints()
  points = vtk.vtkPoints()
  lines = vtk.vtkCellArray()
  for controlPointIndex in range(markupsNode.GetNumberOfControlPoints()):
        points.InsertNextPoint(markupsNode.GetNthControlPointPosition(controlPointIndex))#controlPointIndex])#
        print(markupsNode.GetNthControlPointPosition(controlPointIndex))
  polyline.SetPoints(points)
  # Create a VTK CellArray to define the connectivity (vtk.vtkPolyLine)
  #lines = vtk.vtkCellArray()
  num_points = markupsNode.GetNumberOfControlPoints()
  print(num_points)
  # Create line segments in a loop
  for i in range(num_points-1):
      line = vtk.vtkPolyLine()
      line.GetPointIds().InsertNextId(i)
      line.GetPointIds().InsertNextId(i + 1)
      lines.InsertNextCell(line)
  polyline.SetLines(lines)
  # Create an array of colors for each segment
  colors = vtk.vtkUnsignedCharArray()
  colors.SetNumberOfComponents(3)  # RGB colors
  colors.SetName("Colors_teeth")
  # Define a unique RGB color for each segment
  for j in range(num_points-1):
      print(f'line{j} is {adjacent_dist[j]}') 
      if adjacent_dist[j]<1.5:
        #r, g, b = np.random.randint(0, 256, 3)  # Random RGB color for each segment
        colors.InsertNextTuple3(255, 0, 0)
      else:
         colors.InsertNextTuple3(0, 255, 0)
      #print(r,g,b)
  polyline.GetCellData().SetScalars(colors)
  polyline.Modified()

  modelNode.GetDisplayNode().SetActiveScalar("Colors_teeth", vtk.vtkAssignAttribute.CELL_DATA)
  modelNode.GetDisplayNode().SetAndObserveColorNodeID("")
  modelNode.GetDisplayNode().SetScalarVisibility(True)
  modelNode.GetDisplayNode().SetScalarRangeFlag(slicer.vtkMRMLDisplayNode.UseDirectMapping)
  modelNode.GetDisplayNode().SetLineWidth(5)

pt_observer = markupsNode.AddObserver(slicer.vtkMRMLMarkupsNode.PointModifiedEvent,updatemodelcolor2)


2 Likes