/*=========================================================================

 Copyright (c) ProxSim ltd., Kwun Tong, Hong Kong. All Rights Reserved.

 See COPYRIGHT.txt
 or http://www.slicer.org/copyright/copyright.txt for details.

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.

 This file was originally developed by Davide Punzo, punzodavide@hotmail.it,
 and development was supported by ProxSim ltd.

=========================================================================*/

// VTK includes
#include "vtkCamera.h"
#include "vtkCellArray.h"
#include "vtkCellLocator.h"
#include "vtkConeSource.h"
#include "vtkDoubleArray.h"
#include "vtkDiscretizableColorTransferFunction.h"
#include "vtkFloatArray.h"
#include "vtkGlyph2D.h"
#include "vtkLabelPlacementMapper.h"
#include "vtkLine.h"
#include "vtkMarkupsGlyphSource2D.h"
#include "vtkMath.h"
#include "vtkMatrix4x4.h"
#include "vtkObjectFactory.h"
#include "vtkPiecewiseFunction.h"
#include "vtkPlane.h"
#include "vtkPointData.h"
#include "vtkPointSetToLabelHierarchy.h"
#include "vtkPolyDataMapper2D.h"
#include "vtkProperty2D.h"
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkSlicerMarkupsWidgetRepresentation2D.h"
#include "vtkSphereSource.h"
#include "vtkStringArray.h"
#include "vtkTextActor.h"
#include "vtkTextProperty.h"
#include "vtkTransform.h"

// MRML includes
#include <vtkMRMLFolderDisplayNode.h>
#include <vtkMRMLInteractionEventData.h>
#include <vtkMRMLProceduralColorNode.h>

#include <cmath>
#include <vector>

vtkSlicerMarkupsWidgetRepresentation2D::ControlPointsPipeline2D::ControlPointsPipeline2D()
{
  this->Glypher = vtkSmartPointer<vtkGlyph2D>::New();
  this->Glypher->SetInputData(this->ControlPointsPolyData);
  this->Glypher->SetScaleFactor(1.0);

  // By default the Points are rendered as spheres
  this->Glypher->SetSourceConnection(this->GlyphSourceSphere->GetOutputPort());

  this->Property = vtkSmartPointer<vtkProperty2D>::New();
  this->Property->SetColor(0.4, 1.0, 1.0);
  this->Property->SetPointSize(3.);
  this->Property->SetLineWidth(3.);
  this->Property->SetOpacity(1.);

  this->Mapper = vtkSmartPointer<vtkPolyDataMapper2D>::New();
  this->Mapper->SetInputConnection(this->Glypher->GetOutputPort());
  this->Mapper->ScalarVisibilityOff();
  vtkNew<vtkCoordinate> coordinate;
  // coordinate->SetViewport(this->Renderer); TODO: check this
  //  World coordinates of the node can not be used to build the actors of the representation
  //  Because the world coordinate in the node are the 3D ones.
  coordinate->SetCoordinateSystemToDisplay();
  this->Mapper->SetTransformCoordinate(coordinate);

  this->Actor = vtkSmartPointer<vtkActor2D>::New();
  this->Actor->SetMapper(this->Mapper);
  this->Actor->SetProperty(this->Property);

  // Labels
  this->LabelsMapper = vtkSmartPointer<vtkLabelPlacementMapper>::New();

  // PointSetToLabelHierarchyFilter perturbs (slightly modifies) label positions around
  // coincident points, but the result is far from optimal (labels are quite far from
  // the control point and not at the same distance).
  // We set maximum depth from the default 5 to 10 to essentially eliminate
  // this coincident label position perturbation (perturbation may still be visible
  // when zooming into the view very much). Probably it would be cleaner to have on option
  // to disable this label position adjustment in PointSetToLabelHierarchyFilter.
  this->PointSetToLabelHierarchyFilter->SetMaximumDepth(10);

  this->LabelsMapper->PlaceAllLabelsOn();

  this->LabelsMapper->SetInputConnection(this->PointSetToLabelHierarchyFilter->GetOutputPort());
  // Here it will be the best to use Display coordinate system
  // in that way we would not need the additional copy of the polydata in LabelFocalData (in Viewport coordinates)
  // However the LabelsMapper seems not working with the Display coordinates.
  this->LabelsMapper->GetAnchorTransform()->SetCoordinateSystemToNormalizedViewport();
  this->LabelsActor = vtkSmartPointer<vtkActor2D>::New();
  this->LabelsActor->SetMapper(this->LabelsMapper);
}

vtkSlicerMarkupsWidgetRepresentation2D::ControlPointsPipeline2D::~ControlPointsPipeline2D() = default;

//----------------------------------------------------------------------
vtkSlicerMarkupsWidgetRepresentation2D::LineDirectionArrowPipeline2D::LineDirectionArrowPipeline2D()
{
  this->Points = vtkSmartPointer<vtkPoints>::New();

  this->Normals = vtkSmartPointer<vtkDoubleArray>::New();
  this->Normals->SetNumberOfComponents(3);
  this->Normals->SetName("Normals");

  this->SliceDistances = vtkSmartPointer<vtkFloatArray>::New();
  this->SliceDistances->SetName("SliceDistance");

  this->PointsPoly = vtkSmartPointer<vtkPolyData>::New();
  this->PointsPoly->SetPoints(this->Points);
  this->PointsPoly->GetPointData()->AddArray(this->Normals);
  this->PointsPoly->GetPointData()->SetActiveNormals("Normals");
  this->PointsPoly->GetPointData()->AddArray(this->SliceDistances);
  this->PointsPoly->GetPointData()->SetActiveScalars("SliceDistance");

  // 'coneSource' is a local variable; VTK's pipeline keeps it alive as needed.
  vtkNew<vtkConeSource> coneSource;
  coneSource->SetHeight(1.0);
  coneSource->SetRadius(0.4);
  coneSource->SetResolution(6);
  coneSource->SetDirection(1, 0, 0);

  this->Glypher = vtkSmartPointer<vtkGlyph2D>::New();
  this->Glypher->SetInputData(this->PointsPoly);
  this->Glypher->SetSourceConnection(coneSource->GetOutputPort());
  this->Glypher->SetVectorModeToUseNormal();
  this->Glypher->OrientOn();
  this->Glypher->SetScaleModeToDataScalingOff();
  this->Glypher->SetScaleFactor(1.0);

  this->Mapper = vtkSmartPointer<vtkPolyDataMapper2D>::New();
  this->Mapper->SetInputConnection(this->Glypher->GetOutputPort());
  this->Mapper->SetScalarVisibility(true);

  this->Actor = vtkSmartPointer<vtkActor2D>::New();
  this->Actor->SetMapper(this->Mapper);
  this->Actor->SetVisibility(false);
}

//----------------------------------------------------------------------
vtkSlicerMarkupsWidgetRepresentation2D::LineIntersectionPointsPipeline2D::LineIntersectionPointsPipeline2D(vtkAlgorithmOutput* glyphSourcePort, vtkProperty2D* property)
{
  this->DisplayPoints = vtkSmartPointer<vtkPoints>::New();

  this->PointsPoly = vtkSmartPointer<vtkPolyData>::New();
  this->PointsPoly->SetPoints(this->DisplayPoints);

  this->Glypher = vtkSmartPointer<vtkGlyph2D>::New();
  this->Glypher->SetInputData(this->PointsPoly);
  this->Glypher->SetSourceConnection(glyphSourcePort);
  this->Glypher->SetScaleModeToDataScalingOff();
  this->Glypher->SetScaleFactor(1.0);

  this->Mapper = vtkSmartPointer<vtkPolyDataMapper2D>::New();
  this->Mapper->SetInputConnection(this->Glypher->GetOutputPort());
  this->Mapper->SetScalarVisibility(false);
  vtkNew<vtkCoordinate> coord;
  coord->SetCoordinateSystemToDisplay();
  this->Mapper->SetTransformCoordinate(coord);

  this->Property = vtkSmartPointer<vtkProperty2D>::New();
  this->Property->SetColor(property->GetColor());
  this->Property->SetOpacity(property->GetOpacity());
  this->Property->SetPointSize(property->GetPointSize());
  this->Property->SetLineWidth(property->GetLineWidth());

  this->Actor = vtkSmartPointer<vtkActor2D>::New();
  this->Actor->SetMapper(this->Mapper);
  this->Actor->SetProperty(this->Property);
  this->Actor->SetVisibility(false);
}

//----------------------------------------------------------------------
vtkSlicerMarkupsWidgetRepresentation2D::vtkSlicerMarkupsWidgetRepresentation2D()
{
  for (int i = 0; i < NumberOfControlPointTypes; i++)
  {
    this->ControlPoints[i] = new ControlPointsPipeline2D;
  }

  this->ControlPoints[Selected]->TextProperty->SetColor(1.0, 0.5, 0.5);
  reinterpret_cast<ControlPointsPipeline2D*>(this->ControlPoints[Selected])->Property->SetColor(1.0, 0.5, 0.5);

  this->ControlPoints[Active]->TextProperty->SetColor(0.4, 1.0, 0.); // bright green
  reinterpret_cast<ControlPointsPipeline2D*>(this->ControlPoints[Active])->Property->SetColor(0.4, 1.0, 0.);

  this->TextActor->SetTextProperty(this->GetControlPointsPipeline(Unselected)->TextProperty);

  this->PointsVisibilityOnSlice = vtkSmartPointer<vtkIntArray>::New();
  this->PointsVisibilityOnSlice->SetName("pointsVisibilityOnSlice");
  this->PointsVisibilityOnSlice->Allocate(100);
  this->PointsVisibilityOnSlice->SetNumberOfValues(1);
  this->PointsVisibilityOnSlice->SetValue(0, 0);

  this->SlicePlane = vtkSmartPointer<vtkPlane>::New();
  this->WorldToSliceTransform = vtkSmartPointer<vtkTransform>::New();

  // Slice intersection point glyph pipelines
  this->LineSliceIntersectionWorldPoints = vtkSmartPointer<vtkPoints>::New();
  this->LineSliceIntersectionApproachingSigns = vtkSmartPointer<vtkFloatArray>::New();
  this->LineSliceIntersectionApproachingSigns->SetName("ApproachingSign");
  this->LineSliceIntersectionApproachingSigns->SetNumberOfComponents(1);

  // Direction arrow glyph pipeline (2D)
  this->LineDirectionArrowPipeline = std::make_unique<LineDirectionArrowPipeline2D>();
  this->LineDirectionMarkerCachedWorldPositions = vtkSmartPointer<vtkPoints>::New();
  this->LineDirectionMarkerCachedWorldTangents = vtkSmartPointer<vtkDoubleArray>::New();
  this->LineDirectionMarkerCachedWorldTangents->SetNumberOfComponents(3);

  this->LineSliceIntersectionProperty = vtkSmartPointer<vtkProperty2D>::New();
  this->LineSliceIntersectionProperty->SetColor(1.0, 1.0, 1.0);
  this->LineSliceIntersectionProperty->SetOpacity(1.0);
  this->LineSliceIntersectionProperty->SetPointSize(3.0);
  this->LineSliceIntersectionProperty->SetLineWidth(3.0);

  // Entering pipeline (CircledCross glyph)
  // 'enteringGlyphSource' is a local variable; VTK's pipeline keeps it alive as needed.
  vtkNew<vtkMarkupsGlyphSource2D> enteringGlyphSource;
  enteringGlyphSource->SetGlyphTypeToCircledCross();
  enteringGlyphSource->FilledOff();
  this->LineSliceIntersectionEnteringPipeline = std::make_unique<LineIntersectionPointsPipeline2D>(enteringGlyphSource->GetOutputPort(), this->LineSliceIntersectionProperty);

  // Exiting pipeline (CircledPoint glyph)
  // 'exitingGlyphSource' is a local variable; VTK's pipeline keeps it alive as needed.
  vtkNew<vtkMarkupsGlyphSource2D> exitingGlyphSource;
  exitingGlyphSource->SetGlyphTypeToCircledPoint();
  exitingGlyphSource->FilledOff();
  this->LineSliceIntersectionExitingPipeline = std::make_unique<LineIntersectionPointsPipeline2D>(exitingGlyphSource->GetOutputPort(), this->LineSliceIntersectionProperty);
}

//----------------------------------------------------------------------
vtkSlicerMarkupsWidgetRepresentation2D::~vtkSlicerMarkupsWidgetRepresentation2D() = default;

//----------------------------------------------------------------------
void vtkSlicerMarkupsWidgetRepresentation2D::GetSliceToWorldCoordinates(const double slicePos[2], double worldPos[3])
{
  vtkMRMLSliceNode* sliceNode = this->GetSliceNode();
  if (!this->Renderer || !sliceNode)
  {
    return;
  }

  double xyzw[4] = { slicePos[0] - this->Renderer->GetOrigin()[0], slicePos[1] - this->Renderer->GetOrigin()[1], 0.0, 1.0 };
  double rasw[4] = { 0.0, 0.0, 0.0, 1.0 };

  this->GetSliceNode()->GetXYToRAS()->MultiplyPoint(xyzw, rasw);

  worldPos[0] = rasw[0] / rasw[3];
  worldPos[1] = rasw[1] / rasw[3];
  worldPos[2] = rasw[2] / rasw[3];
}

//----------------------------------------------------------------------
void vtkSlicerMarkupsWidgetRepresentation2D::GetWorldToSliceCoordinates(const double worldPos[3], double slicePos[2])
{
  vtkMRMLSliceNode* sliceNode = this->GetSliceNode();
  if (!sliceNode)
  {
    return;
  }

  double sliceCoordinates[4], worldCoordinates[4];
  worldCoordinates[0] = worldPos[0];
  worldCoordinates[1] = worldPos[1];
  worldCoordinates[2] = worldPos[2];
  worldCoordinates[3] = 1.0;

  vtkNew<vtkMatrix4x4> rasToxyMatrix;
  sliceNode->GetXYToRAS()->Invert(sliceNode->GetXYToRAS(), rasToxyMatrix.GetPointer());

  rasToxyMatrix->MultiplyPoint(worldCoordinates, sliceCoordinates);

  slicePos[0] = sliceCoordinates[0];
  slicePos[1] = sliceCoordinates[1];
}

//----------------------------------------------------------------------
void vtkSlicerMarkupsWidgetRepresentation2D::UpdateAllPointsAndLabelsFromMRML(double labelsOffset)
{
  vtkMRMLMarkupsNode* markupsNode = this->GetMarkupsNode();
  if (!this->ViewNode || !markupsNode || !this->MarkupsDisplayNode || !this->Renderer)
  {
    return;
  }

  // Use first active control point for jumping //TODO: Have an 'even more active' point concept
  std::vector<int> activeControlPointIndices;
  this->MarkupsDisplayNode->GetActiveControlPoints(activeControlPointIndices);
  int activeControlPointIndex = -1;
  if (!activeControlPointIndices.empty())
  {
    activeControlPointIndex = activeControlPointIndices[0];
  }

  int numPoints = markupsNode->GetNumberOfControlPoints();

  for (int controlPointType = 0; controlPointType < NumberOfControlPointTypes; ++controlPointType)
  {
    ControlPointsPipeline2D* controlPoints = reinterpret_cast<ControlPointsPipeline2D*>(this->ControlPoints[controlPointType]);

    controlPoints->ControlPoints->Reset();
    controlPoints->ControlPointsPolyData->GetPointData()->GetNormals()->Reset();

    controlPoints->LabelControlPoints->Reset();
    controlPoints->LabelControlPointsPolyData->GetPointData()->GetNormals()->Reset();
    controlPoints->Labels->Reset();
    controlPoints->LabelsPriority->Reset();

    int startIndex = 0;
    int stopIndex = numPoints - 1;
    if (controlPointType == Active)
    {
      if (activeControlPointIndex >= 0 && activeControlPointIndex < numPoints &&        //
          markupsNode->GetNthControlPointPositionVisibility(activeControlPointIndex) && //
          markupsNode->GetNthControlPointPositionVisibility(activeControlPointIndex) && //
          ((this->PointsVisibilityOnSlice->GetValue(activeControlPointIndex) &&         //
            !this->MarkupsDisplayNode->GetSliceProjection())
           || //
           this->MarkupsDisplayNode->GetSliceProjection()))
      {
        startIndex = activeControlPointIndex;
        stopIndex = startIndex;
      }
      else
      {
        controlPoints->Actor->VisibilityOff();
        controlPoints->LabelsActor->VisibilityOff();
        continue;
      }
    }

    if (controlPointType >= Project && //
        !this->MarkupsDisplayNode->GetSliceProjection())
    {
      continue;
    }

    if (controlPointType == ProjectBack && //
        !this->MarkupsDisplayNode->GetSliceProjectionOutlinedBehindSlicePlane())
    {
      continue;
    }

    for (int pointIndex = startIndex; pointIndex <= stopIndex; pointIndex++)
    {
      if (!(markupsNode->GetNthControlPointPositionVisibility(pointIndex) && markupsNode->GetNthControlPointVisibility(pointIndex)) || //
          (controlPointType < Active &&                                                                                                //
           !this->PointsVisibilityOnSlice->GetValue(pointIndex))
          ||                            //
          (controlPointType > Active && //
           this->PointsVisibilityOnSlice->GetValue(pointIndex)))
      {
        continue;
      }
      if (controlPointType == Project &&                                            //
          this->MarkupsDisplayNode->GetSliceProjectionOutlinedBehindSlicePlane() && //
          !this->IsPointInFrontSlice(markupsNode, pointIndex))
      {
        continue;
      }
      if (controlPointType == ProjectBack &&                                        //
          this->MarkupsDisplayNode->GetSliceProjectionOutlinedBehindSlicePlane() && //
          !this->IsPointBehindSlice(markupsNode, pointIndex))
      {
        continue;
      }
      if (controlPointType < Active)
      {
        bool thisNodeSelected = (markupsNode->GetNthControlPointSelected(pointIndex) != 0);
        if ((controlPointType == Selected) != thisNodeSelected)
        {
          continue;
        }
        if (pointIndex == activeControlPointIndex)
        {
          continue;
        }
      }
      if (controlPointType > Active && pointIndex == activeControlPointIndex)
      {
        continue;
      }

      double slicePos[3] = { 0.0 };
      this->GetNthControlPointDisplayPosition(pointIndex, slicePos);

      controlPoints->ControlPoints->InsertNextPoint(slicePos);
      slicePos[0] += labelsOffset / sqrt(2.0);
      slicePos[1] += labelsOffset / sqrt(2.0);
      this->Renderer->SetDisplayPoint(slicePos);
      this->Renderer->DisplayToView();
      double viewPos[3] = { 0.0 };
      this->Renderer->GetViewPoint(viewPos);
      this->Renderer->ViewToNormalizedViewport(viewPos[0], viewPos[1], viewPos[2]);
      controlPoints->LabelControlPoints->InsertNextPoint(viewPos);

      double pointNormalWorld[3] = { 0.0, 0.0, 1.0 };
      markupsNode->GetNthControlPointNormalWorld(pointIndex, pointNormalWorld);
      // probably we should transform this orientation to display coordinate system
      controlPoints->ControlPointsPolyData->GetPointData()->GetNormals()->InsertNextTuple(pointNormalWorld);
      controlPoints->LabelControlPointsPolyData->GetPointData()->GetNormals()->InsertNextTuple(pointNormalWorld);

      controlPoints->Labels->InsertNextValue(markupsNode->GetNthControlPointLabel(pointIndex));
      controlPoints->LabelsPriority->InsertNextValue(std::to_string(pointIndex));
    }

    controlPoints->ControlPoints->Modified();
    controlPoints->ControlPointsPolyData->GetPointData()->GetNormals()->Modified();
    controlPoints->ControlPointsPolyData->Modified();

    controlPoints->LabelControlPoints->Modified();
    controlPoints->LabelControlPointsPolyData->GetPointData()->GetNormals()->Modified();
    controlPoints->LabelControlPointsPolyData->Modified();

    if (controlPointType == Active)
    {
      controlPoints->Actor->VisibilityOn();
      // For backward compatibility, we hide labels if text scale is set to 0.
      controlPoints->LabelsActor->SetVisibility(this->MarkupsDisplayNode->GetPointLabelsVisibility() //
                                                && this->MarkupsDisplayNode->GetTextScale() > 0.0);
    }
  }
}

//----------------------------------------------------------------------
double vtkSlicerMarkupsWidgetRepresentation2D::GetWidgetOpacity(int controlPointType)
{
  if (!this->MarkupsDisplayNode)
  {
    return 1.0;
  }

  // Use hierarchy information if any, and if overriding is allowed for the current display node
  double hierarchyOpacity = 1.0;
  if (this->MarkupsDisplayNode->GetFolderDisplayOverrideAllowed())
  {
    vtkMRMLDisplayableNode* displayableNode = this->MarkupsDisplayNode->GetDisplayableNode();
    hierarchyOpacity = vtkMRMLFolderDisplayNode::GetHierarchyOpacity(displayableNode);
  }

  switch (controlPointType)
  {
    case Unselected: return this->MarkupsDisplayNode->GetOpacity() * hierarchyOpacity;
    case Selected: return this->MarkupsDisplayNode->GetOpacity() * hierarchyOpacity;
    case Active: return this->MarkupsDisplayNode->GetOpacity() * hierarchyOpacity;
    case Project: return this->MarkupsDisplayNode->GetSliceProjectionOpacity() * hierarchyOpacity;
    case ProjectBack: return this->MarkupsDisplayNode->GetSliceProjectionOpacity() * hierarchyOpacity;
    default: return 1.0;
  }
}

//----------------------------------------------------------------------
vtkMRMLSliceNode* vtkSlicerMarkupsWidgetRepresentation2D::GetSliceNode()
{
  return vtkMRMLSliceNode::SafeDownCast(this->ViewNode);
}

//----------------------------------------------------------------------
int vtkSlicerMarkupsWidgetRepresentation2D::GetNthControlPointDisplayPosition(int n, double slicePos[2])
{
  vtkMRMLSliceNode* sliceNode = this->GetSliceNode();
  vtkMRMLMarkupsNode* markupsNode = this->GetMarkupsNode();
  if (!markupsNode || n < 0 || n >= markupsNode->GetNumberOfControlPoints() || !sliceNode)
  {
    return 0;
  }
  double worldPos[3];
  markupsNode->GetNthControlPointPositionWorld(n, worldPos);
  this->GetWorldToSliceCoordinates(worldPos, slicePos);
  return 1;
}

//----------------------------------------------------------------------
void vtkSlicerMarkupsWidgetRepresentation2D::SetNthControlPointSliceVisibility(int n, bool visibility)
{
  this->PointsVisibilityOnSlice->InsertValue(n, visibility);
  this->Modified();
}

//----------------------------------------------------------------------
bool vtkSlicerMarkupsWidgetRepresentation2D::GetNthControlPointViewVisibility(int n)
{
  vtkMRMLMarkupsNode* markupsNode = this->GetMarkupsNode();
  if (!markupsNode || !this->GetVisibility())
  {
    return false;
  }
  if (!(markupsNode->GetNthControlPointPositionVisibility(n) //
        && (markupsNode->GetNthControlPointVisibility(n))))
  {
    return false;
  }
  return (this->PointsVisibilityOnSlice->GetValue(n) != 0 || this->MarkupsDisplayNode->GetSliceProjection());
}

//----------------------------------------------------------------------
void vtkSlicerMarkupsWidgetRepresentation2D::SetCenterSliceVisibility(bool visibility)
{
  this->CenterVisibilityOnSlice = visibility;
  this->Modified();
}

//----------------------------------------------------------------------
void vtkSlicerMarkupsWidgetRepresentation2D::UpdateFromMRMLInternal(vtkMRMLNode* caller, unsigned long event, void* callData /*=nullptr*/)
{
  Superclass::UpdateFromMRMLInternal(caller, event, callData);

  // Update from slice node
  if (!caller || caller == this->ViewNode.GetPointer())
  {
    this->UpdateViewScaleFactor();
    this->UpdatePlaneFromSliceNode();
  }

  vtkMRMLMarkupsNode* markupsNode = this->GetMarkupsNode();
  if (!markupsNode || !this->IsDisplayable())
  {
    this->VisibilityOff();
    return;
  }

  for (int controlPointType = 0; controlPointType < NumberOfControlPointTypes; ++controlPointType)
  {
    double* color = this->GetWidgetColor(controlPointType);
    double opacity = this->GetWidgetOpacity(controlPointType);

    ControlPointsPipeline2D* controlPoints = this->GetControlPointsPipeline(controlPointType);
    controlPoints->Property->SetColor(color);
    controlPoints->Property->SetOpacity(opacity);

    controlPoints->TextProperty->ShallowCopy(this->MarkupsDisplayNode->GetTextProperty());
    if (this->GetApplicationLogic())
    {
      this->GetApplicationLogic()->UseCustomFontFile(controlPoints->TextProperty);
    }
    controlPoints->TextProperty->SetColor(color);
    controlPoints->TextProperty->SetOpacity(opacity);
    controlPoints->TextProperty->SetFontSize(
      static_cast<int>(this->MarkupsDisplayNode->GetTextProperty()->GetFontSize() * this->MarkupsDisplayNode->GetTextScale() * this->GetScreenScaleFactor() * 5.0));
    controlPoints->TextProperty->SetBackgroundOpacity(opacity * this->MarkupsDisplayNode->GetTextProperty()->GetBackgroundOpacity());

    vtkMarkupsGlyphSource2D* glyphSource = this->GetControlPointsPipeline(controlPointType)->GlyphSource2D;
    if (this->MarkupsDisplayNode->GlyphTypeIs3D())
    {
      // Sphere is the only 3D glyph, use 2D circle in default
      glyphSource->SetGlyphTypeToCircle();
    }
    else
    {
      glyphSource->SetGlyphType(this->GetGlyphTypeSourceFromDisplay(this->MarkupsDisplayNode->GetGlyphType()));
    }

    if (this->MarkupsDisplayNode->GetSliceProjectionOutlinedBehindSlicePlane() && controlPointType == ProjectBack)
    {
      glyphSource->FilledOff();
    }
    else
    {
      glyphSource->FilledOn();
    }
    this->GetControlPointsPipeline(controlPointType)->Glypher->SetSourceConnection(glyphSource->GetOutputPort());
  }

  this->UpdateControlPointSize();

  // Points widgets have only one Markup/Representation
  this->AnyPointVisibilityOnSlice = false;
  for (int pointIndex = 0; pointIndex < markupsNode->GetNumberOfControlPoints(); pointIndex++)
  {
    bool visibility = this->IsControlPointDisplayableOnSlice(markupsNode, pointIndex);
    if (visibility)
    {
      this->AnyPointVisibilityOnSlice = true;
    }
    this->SetNthControlPointSliceVisibility(pointIndex, visibility);
  }

  if (markupsNode->GetCurveClosed())
  {
    bool visibility = this->IsCenterDisplayableOnSlice(markupsNode);
    this->SetCenterSliceVisibility(visibility);
  }

  for (int controlPointType = 0; controlPointType < NumberOfControlPointTypes; ++controlPointType)
  {
    ControlPointsPipeline2D* controlPoints = reinterpret_cast<ControlPointsPipeline2D*>(this->ControlPoints[controlPointType]);
    // For backward compatibility, we hide labels if text scale is set to 0.
    controlPoints->LabelsActor->SetVisibility(this->MarkupsDisplayNode->GetPointLabelsVisibility() //
                                              && this->MarkupsDisplayNode->GetTextScale() > 0.0);
    controlPoints->Glypher->SetScaleFactor(this->ControlPointSize);
  }

  // put the labels near the boundary of the glyph, slightly away from it (by half picking tolarance)
  double labelsOffset = this->ControlPointSize * 0.5 + this->PickingTolerance * 0.5 * this->GetScreenScaleFactor();
  this->UpdateAllPointsAndLabelsFromMRML(labelsOffset);

  this->VisibilityOn();
}

//----------------------------------------------------------------------
void vtkSlicerMarkupsWidgetRepresentation2D::CanInteract(vtkMRMLInteractionEventData* interactionEventData,
                                                         int& foundComponentType,
                                                         int& foundComponentIndex,
                                                         double& closestDistance2)
{
  foundComponentType = vtkMRMLMarkupsDisplayNode::ComponentNone;
  vtkMRMLSliceNode* sliceNode = this->GetSliceNode();
  vtkMRMLMarkupsNode* markupsNode = this->GetMarkupsNode();
  if (!sliceNode || !markupsNode || markupsNode->GetLocked() || !this->GetVisibility() || !interactionEventData)
  {
    return;
  }

  const int* displayPosition = interactionEventData->GetDisplayPosition();
  double displayPosition3[3] = { static_cast<double>(displayPosition[0]), static_cast<double>(displayPosition[1]), 0.0 };

  this->UpdateControlPointSize();
  double maxPickingDistanceFromControlPoint2 = this->GetMaximumControlPointPickingDistance2();

  closestDistance2 = VTK_DOUBLE_MAX; // in display coordinate system
  foundComponentIndex = -1;

  if (markupsNode->GetNumberOfControlPoints() > 2 && this->CurveClosed && this->CenterVisibilityOnSlice)
  {
    // Check if center is selected
    double centerPosWorld[3], centerPosDisplay[3];
    markupsNode->GetCenterOfRotationWorld(centerPosWorld);
    this->GetWorldToSliceCoordinates(centerPosWorld, centerPosDisplay);

    double dist2 = vtkMath::Distance2BetweenPoints(centerPosDisplay, displayPosition3);
    if (dist2 < maxPickingDistanceFromControlPoint2)
    {
      closestDistance2 = dist2;
      foundComponentType = vtkMRMLMarkupsDisplayNode::ComponentCenterPoint;
      foundComponentIndex = 0;
    }
  }

  vtkIdType numberOfPoints = markupsNode->GetNumberOfControlPoints();

  double pointDisplayPos[4] = { 0.0, 0.0, 0.0, 1.0 };
  double pointWorldPos[4] = { 0.0, 0.0, 0.0, 1.0 };

  vtkNew<vtkMatrix4x4> rasToxyMatrix;
  sliceNode->GetXYToRAS()->Invert(sliceNode->GetXYToRAS(), rasToxyMatrix.GetPointer());
  for (int i = 0; i < numberOfPoints; i++)
  {
    if (!this->GetNthControlPointViewVisibility(i))
    {
      continue;
    }
    markupsNode->GetNthControlPointPositionWorld(i, pointWorldPos);
    rasToxyMatrix->MultiplyPoint(pointWorldPos, pointDisplayPos);
    if (this->MarkupsDisplayNode->GetSliceProjection())
    {
      pointDisplayPos[2] = displayPosition3[2];
    }
    double dist2 = vtkMath::Distance2BetweenPoints(pointDisplayPos, displayPosition3);
    if (dist2 < maxPickingDistanceFromControlPoint2 && dist2 < closestDistance2)
    {
      closestDistance2 = dist2;
      foundComponentType = vtkMRMLMarkupsDisplayNode::ComponentControlPoint;
      foundComponentIndex = i;
    }
  }
}

//----------------------------------------------------------------------
void vtkSlicerMarkupsWidgetRepresentation2D::CanInteractWithLine(vtkMRMLInteractionEventData* interactionEventData,
                                                                 int& foundComponentType,
                                                                 int& foundComponentIndex,
                                                                 double& closestDistance2)
{
  foundComponentType = vtkMRMLMarkupsDisplayNode::ComponentNone;

  vtkMRMLSliceNode* sliceNode = this->GetSliceNode();
  vtkMRMLMarkupsNode* markupsNode = this->GetMarkupsNode();
  if (!sliceNode || !markupsNode || markupsNode->GetLocked() || markupsNode->GetNumberOfControlPoints() < 1 //
      || !this->GetVisibility() || !interactionEventData)
  {
    return;
  }

  const int* displayPosition = interactionEventData->GetDisplayPosition();
  double displayPosition3[3] = { static_cast<double>(displayPosition[0]), static_cast<double>(displayPosition[1]), 0.0 };
  double maxPickingDistanceFromControlPoint2 = this->GetMaximumControlPointPickingDistance2();

  vtkIdType numberOfPoints = markupsNode->GetNumberOfControlPoints();

  double pointDisplayPos1[4] = { 0.0, 0.0, 0.0, 1.0 };
  double pointWorldPos1[4] = { 0.0, 0.0, 0.0, 1.0 };
  double pointDisplayPos2[4] = { 0.0, 0.0, 0.0, 1.0 };
  double pointWorldPos2[4] = { 0.0, 0.0, 0.0, 1.0 };

  vtkNew<vtkMatrix4x4> rasToxyMatrix;
  sliceNode->GetXYToRAS()->Invert(sliceNode->GetXYToRAS(), rasToxyMatrix.GetPointer());
  for (int i = 0; i < numberOfPoints - 1; i++)
  {
    if (!this->PointsVisibilityOnSlice->GetValue(i))
    {
      continue;
    }
    if (!this->PointsVisibilityOnSlice->GetValue(i + 1))
    {
      i++; // skip one more, as the next iteration would use (i+1)-th point
      continue;
    }
    markupsNode->GetNthControlPointPositionWorld(i, pointWorldPos1);
    rasToxyMatrix->MultiplyPoint(pointWorldPos1, pointDisplayPos1);
    markupsNode->GetNthControlPointPositionWorld(i + 1, pointWorldPos2);
    rasToxyMatrix->MultiplyPoint(pointWorldPos2, pointDisplayPos2);

    double relativePositionAlongLine = -1.0; // between 0.0-1.0 if between the endpoints of the line segment
    double distance2 = vtkLine::DistanceToLine(displayPosition3, pointDisplayPos1, pointDisplayPos2, relativePositionAlongLine);
    if (distance2 < maxPickingDistanceFromControlPoint2 && distance2 < closestDistance2 && relativePositionAlongLine >= 0 && relativePositionAlongLine <= 1)
    {
      closestDistance2 = distance2;
      foundComponentType = vtkMRMLMarkupsDisplayNode::ComponentLine;
      foundComponentIndex = i;
    }
  }
}

//----------------------------------------------------------------------
void vtkSlicerMarkupsWidgetRepresentation2D::GetActors(vtkPropCollection* pc)
{
  Superclass::GetActors(pc);
  this->LineDirectionArrowPipeline->Actor->GetActors(pc);
  for (int i = 0; i < NumberOfControlPointTypes; i++)
  {
    ControlPointsPipeline2D* controlPoints = reinterpret_cast<ControlPointsPipeline2D*>(this->ControlPoints[i]);
    controlPoints->Actor->GetActors(pc);
    controlPoints->LabelsActor->GetActors(pc);
  }
  this->TextActor->GetActors(pc);
  this->LineSliceIntersectionEnteringPipeline->Actor->GetActors(pc);
  this->LineSliceIntersectionExitingPipeline->Actor->GetActors(pc);
}

//----------------------------------------------------------------------
void vtkSlicerMarkupsWidgetRepresentation2D::ReleaseGraphicsResources(vtkWindow* win)
{
  Superclass::ReleaseGraphicsResources(win);
  this->LineDirectionArrowPipeline->Actor->ReleaseGraphicsResources(win);
  for (int i = 0; i < NumberOfControlPointTypes; i++)
  {
    ControlPointsPipeline2D* controlPoints = reinterpret_cast<ControlPointsPipeline2D*>(this->ControlPoints[i]);
    controlPoints->Actor->ReleaseGraphicsResources(win);
    controlPoints->LabelsActor->ReleaseGraphicsResources(win);
  }
  this->TextActor->ReleaseGraphicsResources(win);
  this->LineSliceIntersectionEnteringPipeline->Actor->ReleaseGraphicsResources(win);
  this->LineSliceIntersectionExitingPipeline->Actor->ReleaseGraphicsResources(win);
}

//----------------------------------------------------------------------
int vtkSlicerMarkupsWidgetRepresentation2D::RenderOverlay(vtkViewport* viewport)
{
  int count = Superclass::RenderOverlay(viewport);
  if (this->LineDirectionArrowPipeline->Actor->GetVisibility())
  {
    count += this->LineDirectionArrowPipeline->Actor->RenderOverlay(viewport);
  }
  for (int i = 0; i < NumberOfControlPointTypes; i++)
  {
    ControlPointsPipeline2D* controlPoints = reinterpret_cast<ControlPointsPipeline2D*>(this->ControlPoints[i]);
    if (controlPoints->Actor->GetVisibility())
    {
      count += controlPoints->Actor->RenderOverlay(viewport);
    }
    if (controlPoints->LabelsActor->GetVisibility())
    {
      count += controlPoints->LabelsActor->RenderOverlay(viewport);
    }
  }
  if (this->TextActor->GetVisibility())
  {
    count += this->TextActor->RenderOverlay(viewport);
  }
  if (this->LineSliceIntersectionEnteringPipeline->Actor->GetVisibility())
  {
    count += this->LineSliceIntersectionEnteringPipeline->Actor->RenderOverlay(viewport);
  }
  if (this->LineSliceIntersectionExitingPipeline->Actor->GetVisibility())
  {
    count += this->LineSliceIntersectionExitingPipeline->Actor->RenderOverlay(viewport);
  }
  return count;
}

//-----------------------------------------------------------------------------
int vtkSlicerMarkupsWidgetRepresentation2D::RenderOpaqueGeometry(vtkViewport* viewport)
{
  int count = 0;
  if (this->LineDirectionArrowPipeline->Actor->GetVisibility())
  {
    count += this->LineDirectionArrowPipeline->Actor->RenderOpaqueGeometry(viewport);
  }
  if (this->TextActor->GetVisibility())
  {
    count += this->TextActor->RenderOpaqueGeometry(viewport);
  }
  for (int i = 0; i < NumberOfControlPointTypes; i++)
  {
    ControlPointsPipeline2D* controlPoints = reinterpret_cast<ControlPointsPipeline2D*>(this->ControlPoints[i]);
    if (controlPoints->Actor->GetVisibility())
    {
      count += controlPoints->Actor->RenderOpaqueGeometry(viewport);
    }
    if (controlPoints->LabelsActor->GetVisibility())
    {
      count += controlPoints->LabelsActor->RenderOpaqueGeometry(viewport);
    }
  }
  if (this->LineSliceIntersectionEnteringPipeline->Actor->GetVisibility())
  {
    count += this->LineSliceIntersectionEnteringPipeline->Actor->RenderOpaqueGeometry(viewport);
  }
  if (this->LineSliceIntersectionExitingPipeline->Actor->GetVisibility())
  {
    count += this->LineSliceIntersectionExitingPipeline->Actor->RenderOpaqueGeometry(viewport);
  }
  return count;
}

//-----------------------------------------------------------------------------
int vtkSlicerMarkupsWidgetRepresentation2D::RenderTranslucentPolygonalGeometry(vtkViewport* viewport)
{
  int count = Superclass::RenderTranslucentPolygonalGeometry(viewport);
  if (this->LineDirectionArrowPipeline->Actor->GetVisibility())
  {
    count += this->LineDirectionArrowPipeline->Actor->RenderTranslucentPolygonalGeometry(viewport);
  }
  if (this->TextActor->GetVisibility())
  {
    count += this->TextActor->RenderTranslucentPolygonalGeometry(viewport);
  }
  for (int i = 0; i < NumberOfControlPointTypes; i++)
  {
    ControlPointsPipeline2D* controlPoints = reinterpret_cast<ControlPointsPipeline2D*>(this->ControlPoints[i]);
    if (controlPoints->Actor->GetVisibility())
    {
      count += controlPoints->Actor->RenderTranslucentPolygonalGeometry(viewport);
    }
    if (controlPoints->LabelsActor->GetVisibility())
    {
      count += controlPoints->LabelsActor->RenderTranslucentPolygonalGeometry(viewport);
    }
  }
  if (this->LineSliceIntersectionEnteringPipeline->Actor->GetVisibility())
  {
    count += this->LineSliceIntersectionEnteringPipeline->Actor->RenderTranslucentPolygonalGeometry(viewport);
  }
  if (this->LineSliceIntersectionExitingPipeline->Actor->GetVisibility())
  {
    count += this->LineSliceIntersectionExitingPipeline->Actor->RenderTranslucentPolygonalGeometry(viewport);
  }
  return count;
}

//-----------------------------------------------------------------------------
vtkTypeBool vtkSlicerMarkupsWidgetRepresentation2D::HasTranslucentPolygonalGeometry()
{
  if (this->Superclass::HasTranslucentPolygonalGeometry())
  {
    return true;
  }
  if (this->LineDirectionArrowPipeline->Actor->GetVisibility() && this->LineDirectionArrowPipeline->Actor->HasTranslucentPolygonalGeometry())
  {
    return true;
  }
  if (this->TextActor->GetVisibility() && this->TextActor->HasTranslucentPolygonalGeometry())
  {
    return true;
  }
  for (int i = 0; i < NumberOfControlPointTypes; i++)
  {
    ControlPointsPipeline2D* controlPoints = reinterpret_cast<ControlPointsPipeline2D*>(this->ControlPoints[i]);
    if (controlPoints->Actor->GetVisibility() && controlPoints->Actor->HasTranslucentPolygonalGeometry())
    {
      return true;
    }
    if (controlPoints->LabelsActor->GetVisibility() && controlPoints->LabelsActor->HasTranslucentPolygonalGeometry())
    {
      return true;
    }
  }
  if (this->LineSliceIntersectionEnteringPipeline->Actor->GetVisibility() && this->LineSliceIntersectionEnteringPipeline->Actor->HasTranslucentPolygonalGeometry())
  {
    return true;
  }
  if (this->LineSliceIntersectionExitingPipeline->Actor->GetVisibility() && this->LineSliceIntersectionExitingPipeline->Actor->HasTranslucentPolygonalGeometry())
  {
    return true;
  }
  return false;
}

//-----------------------------------------------------------------------------
void vtkSlicerMarkupsWidgetRepresentation2D::PrintSelf(ostream& os, vtkIndent indent)
{
  // Superclass typedef defined in vtkTypeMacro() found in vtkSetGet.h
  this->Superclass::PrintSelf(os, indent);

  if (this->TextActor)
  {
    os << indent << "Text Visibility: " << this->TextActor->GetVisibility() << "\n";
  }
  else
  {
    os << indent << "Text Visibility: (none)\n";
  }

  for (int i = 0; i < NumberOfControlPointTypes; i++)
  {
    ControlPointsPipeline2D* controlPoints = reinterpret_cast<ControlPointsPipeline2D*>(this->ControlPoints[i]);
    os << indent << "Pipeline " << i << "\n";
    if (controlPoints->Actor)
    {
      os << indent << "Points Visibility: " << controlPoints->Actor->GetVisibility() << "\n";
    }
    else
    {
      os << indent << "Points: (none)\n";
    }
    if (controlPoints->LabelsActor)
    {
      os << indent << "Labels Visibility: " << controlPoints->LabelsActor->GetVisibility() << "\n";
    }
    else
    {
      os << indent << "Labels Points: (none)\n";
    }
    if (controlPoints->Property)
    {
      os << indent << "Property: " << controlPoints->Property << "\n";
    }
    else
    {
      os << indent << "Property: (none)\n";
    }
    if (this->LineDirectionArrowPipeline->Actor)
    {
      os << indent << "Arrow Glyph Actor 2D Visibility: " << this->LineDirectionArrowPipeline->Actor->GetVisibility() << "\n";
    }
    else
    {
      os << indent << "Arrow Glyph Actor 2D: (none)\n";
    }
    if (this->LineSliceIntersectionEnteringPipeline->Actor)
    {
      os << indent << "Slice entering intersection Glyph Actor 2D Visibility: " << this->LineSliceIntersectionEnteringPipeline->Actor->GetVisibility() << "\n";
    }
    else
    {
      os << indent << "Slice entering intersection Actor 2D: (none)\n";
    }
    if (this->LineSliceIntersectionExitingPipeline->Actor)
    {
      os << indent << "Slice exiting intersection Actor 2D Visibility: " << this->LineSliceIntersectionExitingPipeline->Actor->GetVisibility() << "\n";
    }
    else
    {
      os << indent << "Slice exiting intersection Actor 2D: (none)\n";
    }
  }
}

//-----------------------------------------------------------------------------
vtkSlicerMarkupsWidgetRepresentation2D::ControlPointsPipeline2D* vtkSlicerMarkupsWidgetRepresentation2D::GetControlPointsPipeline(int controlPointType)
{
  return reinterpret_cast<ControlPointsPipeline2D*>(this->ControlPoints[controlPointType]);
}

//---------------------------------------------------------------------------
bool vtkSlicerMarkupsWidgetRepresentation2D::IsControlPointDisplayableOnSlice(vtkMRMLMarkupsNode* markupsNode, int pointIndex)
{
  vtkMRMLSliceNode* sliceNode = this->GetSliceNode();
  if (!sliceNode)
  {
    return false;
  }

  // if there's no node, it's not visible
  if (!markupsNode)
  {
    vtkErrorMacro("IsWidgetDisplayableOnSlice: Could not get the markups node.");
    return false;
  }

  bool showPoint = true;

  // allow nodes to appear only in designated viewers
  vtkMRMLDisplayNode* displayNode = markupsNode->GetDisplayNode();
  if (!displayNode)
  {
    return false;
  }

  double transformedWorldCoordinates[4];
  markupsNode->GetNthControlPointPositionWorld(pointIndex, transformedWorldCoordinates);

  // now get the displayCoordinates for the transformed worldCoordinates
  double displayCoordinates[4];
  this->GetWorldToDisplayCoordinates(transformedWorldCoordinates, displayCoordinates);

  // check if the point is close enough to the slice to be shown
  if (showPoint)
  {
    // the third coordinate of the displayCoordinates is the distance to the slice
    double distanceToSlice = displayCoordinates[2];
    double maxDistance = 0.5 + (sliceNode->GetDimensions()[2] - 1);
    /*vtkDebugMacro("Slice node " << sliceNode->GetName()
      << ": distance to slice = " << distanceToSlice
      << ", maxDistance = " << maxDistance
      << "\n\tslice node dimensions[2] = "
      << sliceNode->GetDimensions()[2]);*/
    if (distanceToSlice < -0.5 || distanceToSlice >= maxDistance)
    {
      // if the distance to the slice is more than 0.5mm, we know that at least one coordinate of the widget is outside the current activeSlice
      // hence, we do not want to show this widget
      showPoint = false;
    }
  }

  return showPoint;
}

//---------------------------------------------------------------------------
bool vtkSlicerMarkupsWidgetRepresentation2D::IsPointBehindSlice(vtkMRMLMarkupsNode* markupsNode, int pointIndex)
{
  vtkMRMLSliceNode* sliceNode = this->GetSliceNode();
  if (!sliceNode)
  {
    return false;
  }

  // if there's no node, it's not visible
  if (!markupsNode)
  {
    vtkErrorMacro("IsWidgetDisplayableOnSlice: Could not get the markups node.");
    return false;
  }

  // allow nodes to appear only in designated viewers
  vtkMRMLDisplayNode* displayNode = markupsNode->GetDisplayNode();
  if (!displayNode)
  {
    return false;
  }

  double transformedWorldCoordinates[4];
  markupsNode->GetNthControlPointPositionWorld(pointIndex, transformedWorldCoordinates);

  // now get the displayCoordinates for the transformed worldCoordinates
  double displayCoordinates[4];
  this->GetWorldToDisplayCoordinates(transformedWorldCoordinates, displayCoordinates);

  // the third coordinate of the displayCoordinates is the distance to the slice
  double distanceToSlice = displayCoordinates[2];
  bool pointBehind = distanceToSlice < -0.5;
  return pointBehind;
}

//---------------------------------------------------------------------------
bool vtkSlicerMarkupsWidgetRepresentation2D::IsPointInFrontSlice(vtkMRMLMarkupsNode* markupsNode, int pointIndex)
{
  vtkMRMLSliceNode* sliceNode = this->GetSliceNode();
  if (!sliceNode)
  {
    return false;
  }

  // if there's no node, it's not visible
  if (!markupsNode)
  {
    vtkErrorMacro("IsWidgetDisplayableOnSlice: Could not get the markups node.");
    return false;
  }

  // allow nodes to appear only in designated viewers
  vtkMRMLDisplayNode* displayNode = markupsNode->GetDisplayNode();
  if (!displayNode)
  {
    return false;
  }

  double transformedWorldCoordinates[4];
  markupsNode->GetNthControlPointPositionWorld(pointIndex, transformedWorldCoordinates);

  // now get the displayCoordinates for the transformed worldCoordinates
  double displayCoordinates[4];
  this->GetWorldToDisplayCoordinates(transformedWorldCoordinates, displayCoordinates);

  // the third coordinate of the displayCoordinates is the distance to the slice
  double distanceToSlice = displayCoordinates[2];
  bool pointFront = distanceToSlice > 0.5;
  return pointFront;
}

//---------------------------------------------------------------------------
bool vtkSlicerMarkupsWidgetRepresentation2D::IsCenterDisplayableOnSlice(vtkMRMLMarkupsNode* markupsNode)
{
  // if no slice node, it doesn't constrain the visibility, so return that
  // it's visible
  vtkMRMLSliceNode* sliceNode = this->GetSliceNode();
  if (!sliceNode)
  {
    vtkErrorMacro("IsWidgetDisplayableOnSlice: Could not get the sliceNode.");
    return false;
  }

  // if there's no node, it's not visible
  if (!markupsNode)
  {
    vtkErrorMacro("IsWidgetDisplayableOnSlice: Could not get the markups node.");
    return false;
  }

  bool showPoint = true;

  // allow nodes to appear only in designated viewers
  if (!markupsNode || !this->IsDisplayable())
  {
    return false;
  }

  // down cast the node as a controlpoints node to get the coordinates
  double transformedWorldCoordinates[4];
  markupsNode->GetCenterOfRotationWorld(transformedWorldCoordinates);

  // now get the displayCoordinates for the transformed worldCoordinates
  double displayCoordinates[4];
  this->GetWorldToDisplayCoordinates(transformedWorldCoordinates, displayCoordinates);

  // check if the point is close enough to the slice to be shown
  if (showPoint)
  {
    // the third coordinate of the displayCoordinates is the distance to the slice
    double distanceToSlice = displayCoordinates[2];
    double maxDistance = 0.5 + (sliceNode->GetDimensions()[2] - 1);
    /*vtkDebugMacro("Slice node " << sliceNode->GetName()
      << ": distance to slice = " << distanceToSlice
      << ", maxDistance = " << maxDistance
      << "\n\tslice node dimensions[2] = "
      << sliceNode->GetDimensions()[2]);*/
    if (distanceToSlice < -0.5 || distanceToSlice >= maxDistance)
    {
      // if the distance to the slice is more than 0.5mm, we know that at least one coordinate of the widget is outside the current activeSlice
      // hence, we do not want to show this widget
      showPoint = false;
    }
  }

  return showPoint;
}

//---------------------------------------------------------------------------
/// Convert world to display coordinates
void vtkSlicerMarkupsWidgetRepresentation2D::GetWorldToDisplayCoordinates(double r, double a, double s, double* displayCoordinates)
{
  vtkMRMLSliceNode* sliceNode = this->GetSliceNode();
  if (!sliceNode)
  {
    vtkErrorMacro("GetWorldToDisplayCoordinates: no slice node!");
    return;
  }

  // we will get the transformation matrix to convert world coordinates to the display coordinates of the specific sliceNode

  vtkMatrix4x4* xyToRasMatrix = sliceNode->GetXYToRAS();
  vtkNew<vtkMatrix4x4> rasToXyMatrix;

  // we need to invert this matrix
  xyToRasMatrix->Invert(xyToRasMatrix, rasToXyMatrix.GetPointer());

  double worldCoordinates[4];
  worldCoordinates[0] = r;
  worldCoordinates[1] = a;
  worldCoordinates[2] = s;
  worldCoordinates[3] = 1;

  rasToXyMatrix->MultiplyPoint(worldCoordinates, displayCoordinates);
  xyToRasMatrix = nullptr;
}

//---------------------------------------------------------------------------
// Convert world to display coordinates
void vtkSlicerMarkupsWidgetRepresentation2D::GetWorldToDisplayCoordinates(double* worldCoordinates, double* displayCoordinates)
{
  if (worldCoordinates == nullptr)
  {
    return;
  }

  this->GetWorldToDisplayCoordinates(worldCoordinates[0], worldCoordinates[1], worldCoordinates[2], displayCoordinates);
}

//----------------------------------------------------------------------
bool vtkSlicerMarkupsWidgetRepresentation2D::GetAllControlPointsVisible()
{
  vtkMRMLMarkupsNode* markupsNode = this->GetMarkupsNode();
  if (!markupsNode)
  {
    return false;
  }

  for (int controlPointIndex = 0; controlPointIndex < markupsNode->GetNumberOfControlPoints(); controlPointIndex++)
  {
    if (!this->PointsVisibilityOnSlice->GetValue(controlPointIndex) ||         //
        !(markupsNode->GetNthControlPointPositionVisibility(controlPointIndex) //
          && (markupsNode->GetNthControlPointVisibility(controlPointIndex))))
    {
      return false;
    }
  }
  return true;
}

//----------------------------------------------------------------------
void vtkSlicerMarkupsWidgetRepresentation2D::UpdateDistanceColorMap(vtkDiscretizableColorTransferFunction* colormap, double color[3])
{
  if (colormap == nullptr || this->MarkupsDisplayNode == nullptr)
  {
    return;
  }

  double limit = this->MarkupsDisplayNode->GetLineColorFadingEnd();
  double tolerance = this->MarkupsDisplayNode->GetLineColorFadingStart();
  vtkPiecewiseFunction* opacityFunction = colormap->GetScalarOpacityFunction();
  if (!opacityFunction)
  {
    opacityFunction = vtkPiecewiseFunction::New();
    colormap->SetScalarOpacityFunction(opacityFunction);
    opacityFunction->Delete();
  }
  double opacity = this->MarkupsDisplayNode->GetOpacity();
  opacityFunction->RemoveAllPoints();
  opacityFunction->AddPoint(-limit, 0.);
  opacityFunction->AddPoint(-tolerance, opacity);
  opacityFunction->AddPoint(tolerance, opacity);
  opacityFunction->AddPoint(limit, 0.);

  colormap->RemoveAllPoints();
  double color1[3] = { std::min(color[0] * 0.5, 1.0), std::min(color[1] * 0.5, 1.0), std::min(color[2] * 2.0, 1.0) };
  double color3[3] = { std::min(color[0] * 2.0, 1.0), std::min(color[1] * 0.5, 1.0), std::min(color[2] * 0.5, 1.0) };

  double colorHsv1[3];
  double colorHsv2[3];
  double colorHsv3[3];
  vtkMath::RGBToHSV(color1, colorHsv1);
  vtkMath::RGBToHSV(color, colorHsv2);
  vtkMath::RGBToHSV(color3, colorHsv3);

  double hueOffset = this->MarkupsDisplayNode->GetLineColorFadingHueOffset();
  double saturation = this->MarkupsDisplayNode->GetLineColorFadingSaturation();

  colormap->AddHSVPoint(-tolerance * 2.0, colorHsv1[0] - hueOffset, saturation * colorHsv1[1], colorHsv1[2]);
  colormap->AddHSVPoint(-tolerance, colorHsv2[0] - hueOffset, saturation * colorHsv2[1], colorHsv2[2]);
  colormap->AddHSVPoint(tolerance, colorHsv2[0] - hueOffset, saturation * colorHsv2[1], colorHsv2[2]);
  colormap->AddHSVPoint(tolerance * 2.0, colorHsv3[0] - hueOffset, saturation * colorHsv3[1], colorHsv3[2]);
  colormap->SetEnableOpacityMapping(true);
  colormap->SetClamping(true);
}

//----------------------------------------------------------------------
void vtkSlicerMarkupsWidgetRepresentation2D::UpdatePlaneFromSliceNode()
{
  vtkMatrix4x4* sliceXYToRAS = this->GetSliceNode()->GetXYToRAS();

  // Update transformation to slice
  vtkNew<vtkMatrix4x4> rasToSliceXY;
  vtkMatrix4x4::Invert(sliceXYToRAS, rasToSliceXY.GetPointer());
  // Project all points to the slice plane (slice Z coordinate = 0)
  rasToSliceXY->SetElement(2, 0, 0);
  rasToSliceXY->SetElement(2, 1, 0);
  rasToSliceXY->SetElement(2, 2, 0);
  this->WorldToSliceTransform->SetMatrix(rasToSliceXY.GetPointer());

  // Update slice plane (for distance computation)
  double normal[3];
  double origin[3];
  const double planeOrientation = 1.0; // +/-1: orientation of the normal
  for (int i = 0; i < 3; i++)
  {
    normal[i] = planeOrientation * sliceXYToRAS->GetElement(i, 2);
    origin[i] = sliceXYToRAS->GetElement(i, 3);
  }
  vtkMath::Normalize(normal);
  this->SlicePlane->SetNormal(normal);
  this->SlicePlane->SetOrigin(origin);
  this->SlicePlane->Modified();
}

//----------------------------------------------------------------------
void vtkSlicerMarkupsWidgetRepresentation2D::UpdateViewScaleFactor()
{
  this->ViewScaleFactorMmPerPixel = 1.0;
  this->ScreenSizePixel = 1000.0;
  if (!this->Renderer || !this->Renderer->GetActiveCamera() || !this->GetSliceNode())
  {
    return;
  }

  const int* screenSize = this->Renderer->GetRenderWindow()->GetScreenSize();
  this->ScreenSizePixel = sqrt(screenSize[0] * screenSize[0] + screenSize[1] * screenSize[1]);

  vtkMatrix4x4* xyToSlice = this->GetSliceNode()->GetXYToSlice();
  this->ViewScaleFactorMmPerPixel = sqrt(xyToSlice->GetElement(0, 1) * xyToSlice->GetElement(0, 1) + xyToSlice->GetElement(1, 1) * xyToSlice->GetElement(1, 1));
}

//----------------------------------------------------------------------
void vtkSlicerMarkupsWidgetRepresentation2D::UpdateControlPointSize()
{
  // Since we use parallel camera projection and the camera scale is 1.0,
  // the renderer coordinate system is the same as the display coordinate system, therefore
  // ControlPointSize is specified in pixels.
  if (this->MarkupsDisplayNode->GetUseGlyphScale())
  {
    // relative
    this->ControlPointSize = this->ScreenSizePixel * this->GetScreenScaleFactor() * this->MarkupsDisplayNode->GetGlyphScale() / 100.0;
  }
  else
  {
    // absolute
    this->ControlPointSize = this->MarkupsDisplayNode->GetGlyphSize() / this->ViewScaleFactorMmPerPixel;
  }
}

//----------------------------------------------------------------------
double vtkSlicerMarkupsWidgetRepresentation2D::GetMaximumControlPointPickingDistance2()
{
  double maximumControlPointPickingDistance = this->ControlPointSize / 2.0 + this->PickingTolerance * this->GetScreenScaleFactor();
  return maximumControlPointPickingDistance * maximumControlPointPickingDistance;
}

//----------------------------------------------------------------------
bool vtkSlicerMarkupsWidgetRepresentation2D::IsRepresentationIntersectingSlice(vtkPolyData* representation, const char* arrayName)
{
  if (!representation || !representation->GetPointData() || representation->GetNumberOfPoints() <= 0)
  {
    return false;
  }

  double sliceNormal_XY[4] = { 0.0, 0.0, 1.0, 0.0 };
  double sliceNormal_World[4] = { 0, 0, 1, 0 };
  vtkMatrix4x4* xyToRAS = this->GetSliceNode()->GetXYToRAS();
  xyToRAS->MultiplyPoint(sliceNormal_XY, sliceNormal_World);
  double sliceThicknessMm = vtkMath::Norm(sliceNormal_World);

  vtkDataArray* distanceArray = representation->GetPointData()->GetArray(arrayName);
  if (!distanceArray)
  {
    return false;
  }
  double* scalarRange = distanceArray->GetRange();

  // If the closest point on the line is further than a half-slice thickness, then hide the markup in 2D
  if (scalarRange[0] > 0.5 * sliceThicknessMm || scalarRange[1] < -0.5 * sliceThicknessMm)
  {
    return false;
  }
  return true;
}

//----------------------------------------------------------------------
void vtkSlicerMarkupsWidgetRepresentation2D::ComputeIntersectionFadingScalars(vtkPolyData* worldPolyData,
                                                                              vtkPlane* slicePlane,
                                                                              vtkPolyData* outputPolyData,
                                                                              vtkFloatArray* distanceArray,
                                                                              vtkPoints* intersectionPoints)
{
  vtkPoints* inputPoints = worldPolyData ? worldPolyData->GetPoints() : nullptr;
  if (!inputPoints || !slicePlane || !outputPolyData || inputPoints->GetNumberOfPoints() < 2)
  {
    if (outputPolyData)
    {
      outputPolyData->Initialize();
    }
    return;
  }

  // Determine the fading range: the tube is fully transparent beyond fadingEnd.
  double fadingEnd = this->MarkupsDisplayNode->GetLineColorFadingEnd();

  // MTime guard: skip recomputation if inputs haven't changed since last call.
  // This avoids expensive polyline walking and subdivision when neither the curve
  // geometry, slice plane position, nor fading range have been modified.
  // When cached, intersectionPoints is left untouched (still valid from previous call).
  vtkMTimeType worldMTime = worldPolyData->GetMTime();
  vtkMTimeType planeMTime = slicePlane->GetMTime();
  if (worldMTime == this->FadingScalarsLastWorldDataMTime && planeMTime == this->FadingScalarsLastSlicePlaneMTime && fadingEnd == this->FadingScalarsLastFadingEnd
      && outputPolyData->GetNumberOfPoints() > 0)
  {
    return;
  }

  // Number of subdivisions within the fading range for smooth opacity gradient
  const int numFadingSteps = 10;
  double subdivisionStep = fadingEnd / numFadingSteps;
  vtkIdType numInputPts = inputPoints->GetNumberOfPoints();

  vtkNew<vtkFloatArray> signedDist;
  if (!distanceArray)
  {
    // Compute signed perpendicular distance from each input point to the slice plane.
    signedDist->SetNumberOfValues(numInputPts);
    slicePlane->EvaluateFunction(inputPoints->GetData(), signedDist);
  }
  else
  {
    signedDist->ShallowCopy(distanceArray);
  }

  // Walk along the polyline, detect plane crossings, and build the output point list.
  // - Insert exact intersection points (scalar = 0) and collect them in intersectionPoints.
  // - Subdivide segments that are near or cross the slice plane so that the per-vertex
  //   opacity mapping produces an accurate fading gradient.
  // - Skip portions of segments that are entirely beyond the fading range (both endpoints
  //   beyond fadingEnd on the same side) to avoid rendering invisible geometry.
  vtkNew<vtkPoints> outPoints;
  outPoints->Allocate(numInputPts * (numFadingSteps + 3));
  vtkNew<vtkFloatArray> outScalars;
  outScalars->SetName("SliceDistance");
  outScalars->SetNumberOfComponents(1);
  outScalars->Allocate(numInputPts * (numFadingSteps + 3));

  // Reset intersection points output for this polydata source.
  if (intersectionPoints)
  {
    intersectionPoints->Reset();
  }
  this->LineSliceIntersectionApproachingSigns->Reset();

  // Add first point (only if within the visible range)
  double prevPt[3];
  inputPoints->GetPoint(0, prevPt);
  bool prevAdded = (fabs(signedDist->GetValue(0)) <= fadingEnd);
  if (prevAdded)
  {
    outPoints->InsertNextPoint(prevPt);
    outScalars->InsertNextValue(static_cast<float>(signedDist->GetValue(0)));
  }

  for (vtkIdType pointIndex = 1; pointIndex < numInputPts; pointIndex++)
  {
    double pt[3];
    inputPoints->GetPoint(pointIndex, pt);

    double d0 = signedDist->GetValue(pointIndex - 1);
    double d1 = signedDist->GetValue(pointIndex);

    // Check if both endpoints are beyond the fading range on the same side.
    // No intersection is possible: segments entirely on one side of the plane
    // that are also beyond the fading range cannot cross the slice plane.
    if ((d0 > fadingEnd && d1 > fadingEnd) || (d0 < -fadingEnd && d1 < -fadingEnd))
    {
      // Entire segment is invisible — skip it
      prevAdded = false;
      vtkMath::Assign(pt, prevPt);
      continue;
    }

    // This segment is at least partially visible. Compute the parameter range [tStart, tEnd]
    // within [0,1] that corresponds to the visible portion (|distance| <= fadingEnd).
    // d(t) = d0 + t*(d1 - d0), so t for a given distance D is: t = (D - d0)/(d1 - d0).
    double tStart = 0.0;
    double tEnd = 1.0;
    double dDiff = d1 - d0;
    bool notInPlane = fabs(dDiff) > 1e-6;
    if (notInPlane)
    {
      // t where distance = +/-fadingEnd
      double tPlus = (fadingEnd - d0) / dDiff;
      double tMinus = (-fadingEnd - d0) / dDiff;

      double tLow = (tPlus < tMinus) ? tPlus : tMinus;
      double tHigh = (tPlus < tMinus) ? tMinus : tPlus;
      tStart = (tLow > tStart) ? tLow : tStart;
      tEnd = (tHigh < tEnd) ? tHigh : tEnd;
    }

    // Clamp to [0, 1]
    tStart = vtkMath::ClampValue(tStart, 0.0, 1.0);
    tEnd = vtkMath::ClampValue(tEnd, 0.0, 1.0);

    if (tStart >= tEnd)
    {
      prevAdded = false;
      vtkMath::Assign(pt, prevPt);
      continue;
    }

    // Compute segment direction vector using vtkMath
    double dir[3];
    vtkMath::Subtract(pt, prevPt, dir);

    // Add the start of the visible portion (if not already added)
    if (tStart > 0.0 || !prevAdded)
    {
      double scaledStart[3] = { dir[0], dir[1], dir[2] };
      vtkMath::MultiplyScalar(scaledStart, tStart);
      double startPt[3];
      vtkMath::Add(prevPt, scaledStart, startPt);
      outPoints->InsertNextPoint(startPt);
      outScalars->InsertNextValue(static_cast<float>(d0 + tStart * dDiff));
    }

    // Find the intersection of this segment with the slice plane (if any)
    double tZero = 0.0;
    double isectPt[3] = { 0.0, 0.0, 0.0 };
    bool hasZeroCrossing = (slicePlane->IntersectWithLine(prevPt, pt, tZero, isectPt) != 0);

    // Collect intersection point for the glyph display pipeline
    if (hasZeroCrossing && intersectionPoints && notInPlane)
    {
      // Only add intersection point if it's not too close to the last one
      vtkIdType numIsectPts = intersectionPoints->GetNumberOfPoints();
      bool addIntersection = true;
      if (numIsectPts > 0)
      {
        double lastIsectPt[3];
        intersectionPoints->GetPoint(numIsectPts - 1, lastIsectPt);
        double dist = vtkMath::Distance2BetweenPoints(isectPt, lastIsectPt);
        // Use a small threshold (e.g., 1e-6) to avoid duplicate points
        if (dist < 1e-6)
        {
          addIntersection = false;
        }
      }
      if (addIntersection)
      {
        intersectionPoints->InsertNextPoint(isectPt);
        this->LineSliceIntersectionApproachingSigns->InsertNextValue(static_cast<float>(d0 > 0. ? 1.0 : -1.0));
      }
    }

    // Subdivide the visible portion [tStart, tEnd] at regular distance intervals
    // and insert the zero crossing at the right position.
    double distRange = fabs(dDiff * (tEnd - tStart));
    int numSubdivisions = static_cast<int>(distRange / subdivisionStep);
    if (numSubdivisions < 1)
    {
      numSubdivisions = 1;
    }
    double tStep = (tEnd - tStart) / numSubdivisions;
    bool zeroCrossingInserted = (!hasZeroCrossing || tZero < tStart || tZero > tEnd);

    for (int s = 1; s <= numSubdivisions; s++)
    {
      double t = tStart + s * tStep;

      // Insert the zero-crossing point at its exact position if we pass it
      if (!zeroCrossingInserted && t >= tZero)
      {
        outPoints->InsertNextPoint(isectPt);
        outScalars->InsertNextValue(0.0f);
        zeroCrossingInserted = true;

        // Don't add a subdivision point if it's essentially at the same location
        if (fabs(t - tZero) < tStep * 0.01)
        {
          continue;
        }
      }

      // Add subdivision point (skip the last one, we'll add the end point below)
      if (s < numSubdivisions)
      {
        double scaledSub[3] = { dir[0], dir[1], dir[2] };
        vtkMath::MultiplyScalar(scaledSub, t);
        double subPt[3];
        vtkMath::Add(prevPt, scaledSub, subPt);
        outPoints->InsertNextPoint(subPt);
        outScalars->InsertNextValue(static_cast<float>(d0 + t * dDiff));
      }
    }

    // Add the end of the visible portion
    double scaledEnd[3] = { dir[0], dir[1], dir[2] };
    vtkMath::MultiplyScalar(scaledEnd, tEnd);
    double endPt[3];
    vtkMath::Add(prevPt, scaledEnd, endPt);
    outPoints->InsertNextPoint(endPt);
    outScalars->InsertNextValue(static_cast<float>(d0 + tEnd * dDiff));
    prevAdded = (tEnd >= 1.0);
    vtkMath::Assign(pt, prevPt);
  }

  // Build polyline cell connecting all points in order
  vtkIdType numOutPts = outPoints->GetNumberOfPoints();
  vtkNew<vtkCellArray> outLines;
  if (numOutPts > 1)
  {
    vtkNew<vtkIdList> ids;
    ids->SetNumberOfIds(numOutPts);
    for (vtkIdType pointIndex = 0; pointIndex < numOutPts; pointIndex++)
    {
      ids->SetId(pointIndex, pointIndex);
    }
    outLines->InsertNextCell(ids);
  }

  outputPolyData->SetPoints(outPoints);
  outputPolyData->SetLines(outLines);
  outputPolyData->GetPointData()->SetScalars(outScalars);
  outputPolyData->Modified();

  // Update cached MTimes so the guard can skip recomputation next time.
  this->FadingScalarsLastWorldDataMTime = worldMTime;
  this->FadingScalarsLastSlicePlaneMTime = planeMTime;
  this->FadingScalarsLastFadingEnd = fadingEnd;
}

//----------------------------------------------------------------------
void vtkSlicerMarkupsWidgetRepresentation2D::UpdateSliceIntersectionPointDisplay(double glyphSize, vtkScalarsToColors* colorMap)
{
  if (!this->MarkupsDisplayNode || !this->MarkupsDisplayNode->GetLineSliceIntersectionPointVisibility() || !this->MarkupsDisplayNode->GetLineDirectionVisibility()
      || !this->MarkupsDisplayNode->GetLineDirectionVisibility2D() || !this->Renderer)
  {
    this->LineSliceIntersectionEnteringPipeline->Actor->SetVisibility(false);
    this->LineSliceIntersectionExitingPipeline->Actor->SetVisibility(false);
    return;
  }

  this->LineSliceIntersectionEnteringPipeline->DisplayPoints->Reset();
  this->LineSliceIntersectionExitingPipeline->DisplayPoints->Reset();

  // Use half of fadingEnd as scalar offset so the LUT picks a clearly
  // distinguishable fading color from the correct side of the slice plane.
  double fadingEnd = this->MarkupsDisplayNode->GetLineColorFadingEnd();
  double sideOffset = fadingEnd * 0.5;
  if (sideOffset <= 0.0)
  {
    sideOffset = 0.01;
  }

  for (vtkIdType pointIndex = 0; pointIndex < this->LineSliceIntersectionWorldPoints->GetNumberOfPoints(); pointIndex++)
  {
    double worldPos[3];
    this->LineSliceIntersectionWorldPoints->GetPoint(pointIndex, worldPos);
    double displayPos[4] = { 0.0 };
    this->GetWorldToDisplayCoordinates(worldPos, displayPos);
    double dp[3] = { displayPos[0], displayPos[1], 0.0 };

    float sign = (pointIndex < this->LineSliceIntersectionApproachingSigns->GetNumberOfTuples()) ? this->LineSliceIntersectionApproachingSigns->GetValue(pointIndex) : 1.0f;
    // When direction markers are reversed, swap entering/exiting assignment
    if (!this->MarkupsDisplayNode->GetLineDirectionFirstToLastControlPoint())
    {
      sign = -sign;
    }

    // Entering: sign > 0 (curve approaches from positive side)
    // Exiting: sign < 0 (curve approaches from negative side)
    if (sign >= 0.0f)
    {
      this->LineSliceIntersectionEnteringPipeline->DisplayPoints->InsertNextPoint(dp);
    }
    else
    {
      this->LineSliceIntersectionExitingPipeline->DisplayPoints->InsertNextPoint(dp);
    }
  }

  // Sample color from the LUT for each side and set on per-pipeline properties.
  // Opacity is not mapped — intersection glyphs are always fully opaque.
  if (colorMap)
  {
    double enteringColor[3];
    colorMap->GetColor(+sideOffset, enteringColor);
    this->LineSliceIntersectionEnteringPipeline->Property->SetColor(enteringColor);

    double exitingColor[3];
    colorMap->GetColor(-sideOffset, exitingColor);
    this->LineSliceIntersectionExitingPipeline->Property->SetColor(exitingColor);
  }

  // Update entering pipeline (CircledCross)
  this->LineSliceIntersectionEnteringPipeline->DisplayPoints->Modified();
  this->LineSliceIntersectionEnteringPipeline->PointsPoly->Modified();
  this->LineSliceIntersectionEnteringPipeline->Glypher->SetScaleFactor(glyphSize);

  // Update exiting pipeline (CircledPoint)
  this->LineSliceIntersectionExitingPipeline->DisplayPoints->Modified();
  this->LineSliceIntersectionExitingPipeline->PointsPoly->Modified();
  this->LineSliceIntersectionExitingPipeline->Glypher->SetScaleFactor(glyphSize);

  this->LineSliceIntersectionEnteringPipeline->Actor->SetVisibility(this->LineSliceIntersectionEnteringPipeline->DisplayPoints->GetNumberOfPoints() > 0);
  this->LineSliceIntersectionExitingPipeline->Actor->SetVisibility(this->LineSliceIntersectionExitingPipeline->DisplayPoints->GetNumberOfPoints() > 0);
}
