iMSTK
Interactive Medical Simulation Toolkit
imstkVTKSurfaceNormalRenderDelegate.cpp
1 /*
2 ** This file is part of the Interactive Medical Simulation Toolkit (iMSTK)
3 ** iMSTK is distributed under the Apache License, Version 2.0.
4 ** See accompanying NOTICE for details.
5 */
6 
7 #include "imstkVTKSurfaceNormalRenderDelegate.h"
8 #include "imstkPointSet.h"
9 #include "imstkVisualModel.h"
10 #include "imstkGeometryUtilities.h"
11 #include "imstkRenderMaterial.h"
12 #include "imstkLogger.h"
13 #include "imstkSurfaceMesh.h"
14 
15 #include <vtkActor.h>
16 #include <vtkDataArray.h>
17 #include <vtkPointData.h>
18 #include <vtkTransform.h>
19 #include <vtkArrowSource.h>
20 #include <vtkOpenGLGlyph3DMapper.h>
21 
22 namespace imstk
23 {
24 VTKSurfaceNormalRenderDelegate::VTKSurfaceNormalRenderDelegate() :
25  m_polydata(vtkSmartPointer<vtkPolyData>::New())
26 {
27 }
28 
29 void
30 VTKSurfaceNormalRenderDelegate::init()
31 {
32  auto surfMesh = std::dynamic_pointer_cast<SurfaceMesh>(m_visualModel->getGeometry());
33  CHECK(surfMesh != nullptr) << "VTKSurfaceNormalRenderDelegate only works with SurfaceMesh geometry";
34  m_surfMeshVertices = surfMesh->getVertexPositions();
35  m_surfMeshIndices = surfMesh->getCells();
36 
37  // Compute the centers of the triangle
38  m_triangleCenterVertices = computeTriangleCenters(m_surfMeshVertices, m_surfMeshIndices);
39  m_triangleNormals = computeTriangleNormals(m_surfMeshVertices, m_surfMeshIndices);
40 
41  // Map vertices to VTK point data
42  if (m_surfMeshVertices != nullptr)
43  {
44  m_mappedVertexArray = GeometryUtils::coupleVtkDataArray(m_triangleCenterVertices);
45  auto points = vtkSmartPointer<vtkPoints>::New();
46  points->SetNumberOfPoints(m_triangleCenterVertices->size());
47  points->SetData(m_mappedVertexArray);
48  m_polydata->SetPoints(points);
49  }
50 
51  m_mappedNormalsArray = GeometryUtils::coupleVtkDataArray(m_triangleNormals);
52  m_mappedNormalsArray->SetName("ImageScalars");
53  m_polydata->GetPointData()->SetVectors(m_mappedNormalsArray);
54 
55  // When geometry is modified, update data source, mostly for when an entirely new array/buffer was set
56  queueConnect<Event>(surfMesh, &Geometry::modified,
57  std::static_pointer_cast<VTKSurfaceNormalRenderDelegate>(shared_from_this()),
59 
60  // When the vertex buffer internals are modified, ie: a single or N elements
61  queueConnect<Event>(m_surfMeshVertices, &AbstractDataArray::modified,
62  std::static_pointer_cast<VTKSurfaceNormalRenderDelegate>(shared_from_this()),
64 
65  // Setup mapper
66  {
67  vtkNew<vtkArrowSource> arrowSource;
68  arrowSource->Update();
69  m_glyphPolyData = arrowSource->GetOutput();
70  vtkNew<vtkOpenGLGlyph3DMapper> mapper;
71  mapper->OrientOn();
72  mapper->SetInputData(m_polydata);
73  mapper->SetSourceData(m_glyphPolyData);
74  mapper->SetOrientationArray(m_mappedNormalsArray->GetName());
75  mapper->ScalingOn();
76  mapper->SetScaleFactor(m_visualModel->getRenderMaterial()->getPointSize());
77  mapper->Update();
78  vtkNew<vtkActor> actor;
79  actor->SetMapper(mapper);
80  actor->SetUserTransform(m_transform);
81  m_mapper = mapper;
82  m_actor = actor;
83  }
84 
85  update();
87 }
88 
89 void
91 {
92  // Custom handling of events
93  std::shared_ptr<PointSet> geom = std::dynamic_pointer_cast<PointSet>(m_visualModel->getGeometry());
94  std::shared_ptr<VecDataArray<double, 3>> vertices = geom->getVertexPositions();
95 
96  // Only use the most recent event from respective sender
97  std::list<Command> cmds;
98  bool contains[4] = { false, false, false, false };
99  rforeachEvent([&](Command cmd)
100  {
101  if (cmd.m_event->m_sender == m_visualModel.get() && !contains[0])
102  {
103  cmds.push_back(cmd);
104  contains[0] = true;
105  }
106  else if (cmd.m_event->m_sender == m_material.get() && !contains[1])
107  {
108  cmds.push_back(cmd);
109  contains[1] = true;
110  }
111  else if (cmd.m_event->m_sender == geom.get() && !contains[2])
112  {
113  cmds.push_back(cmd);
114  contains[2] = true;
115  }
116  else if (cmd.m_event->m_sender == vertices.get() && !contains[3])
117  {
118  cmds.push_back(cmd);
119  contains[3] = true;
120  }
121  });
122 
123  // Now do each event in order recieved
124  for (std::list<Command>::reverse_iterator i = cmds.rbegin(); i != cmds.rend(); i++)
125  {
126  i->invoke();
127  }
128 }
129 
130 void
132 {
133  auto geometry = std::static_pointer_cast<SurfaceMesh>(m_visualModel->getGeometry());
134  m_surfMeshVertices = geometry->getVertexPositions();
135 
136  // Compute the centers of the triangle
137  m_triangleCenterVertices = computeTriangleCenters(m_surfMeshVertices, m_surfMeshIndices);
138  m_triangleNormals = computeTriangleNormals(m_surfMeshVertices, m_surfMeshIndices);
139 
140  m_mappedVertexArray->SetNumberOfComponents(3);
141  m_mappedVertexArray->SetVoidArray(reinterpret_cast<double*>(m_triangleCenterVertices->getPointer()), m_triangleCenterVertices->size() * 3, 1);
142  m_mappedVertexArray->Modified();
143 
144  m_mappedNormalsArray->SetNumberOfComponents(3);
145  m_mappedNormalsArray->SetVoidArray(reinterpret_cast<double*>(m_triangleNormals->getPointer()), m_triangleNormals->size() * 3, 1);
146  m_mappedNormalsArray->Modified();
147 }
148 
149 void
151 {
152  // Called when the geometry posts modified
153  auto geometry = std::static_pointer_cast<PointSet>(m_visualModel->getGeometry());
154 
155  // Test if the vertex buffer changed
156  //if (m_surfMeshVertices != geometry->getVertexPositions())
157  {
158  vertexDataModified(nullptr);
159  }
160 }
161 
162 void
164 {
166 
167  vtkOpenGLGlyph3DMapper::SafeDownCast(m_mapper)->SetScaleFactor(m_visualModel->getRenderMaterial()->getPointSize());
168 }
169 
170 std::shared_ptr<VecDataArray<double, 3>>
172  std::shared_ptr<VecDataArray<double, 3>> verticesPtr,
173  std::shared_ptr<VecDataArray<int, 3>> indicesPtr)
174 {
175  auto newVerticesPtr = std::make_shared<VecDataArray<double, 3>>(indicesPtr->size());
176  VecDataArray<double, 3>& newVertices = *newVerticesPtr;
177 
178  VecDataArray<double, 3>& vertices = *verticesPtr;
179  VecDataArray<int, 3>& indices = *indicesPtr;
180  const double ratio = 1.0 / 3.0;
181  for (int i = 0; i < indices.size(); i++)
182  {
183  const Vec3d& a = vertices[indices[i][0]];
184  const Vec3d& b = vertices[indices[i][1]];
185  const Vec3d& c = vertices[indices[i][2]];
186 
187  newVertices[i] = (a + b + c) * ratio;
188  }
189  return newVerticesPtr;
190 }
191 
192 std::shared_ptr<VecDataArray<double, 3>>
193 VTKSurfaceNormalRenderDelegate::computeTriangleNormals(
194  std::shared_ptr<VecDataArray<double, 3>> verticesPtr,
195  std::shared_ptr<VecDataArray<int, 3>> indicesPtr)
196 {
197  auto orientationsPtr = std::make_shared<VecDataArray<double, 3>>(indicesPtr->size());
198  VecDataArray<double, 3>& orientations = *orientationsPtr;
199 
200  VecDataArray<double, 3>& vertices = *verticesPtr;
201  VecDataArray<int, 3>& indices = *indicesPtr;
202  for (int i = 0; i < indices.size(); i++)
203  {
204  const Vec3d& a = vertices[indices[i][0]];
205  const Vec3d& b = vertices[indices[i][1]];
206  const Vec3d& c = vertices[indices[i][2]];
207 
208  orientations[i] = (c - a).cross(c - b).normalized();
209  }
210  return orientationsPtr;
211 }
212 } // namespace imstk
void rforeachEvent(std::function< void(Command cmd)> func)
thread safe reverse loop over all event commands, one can implement a custom handler ...
void update()
Update render delegate.
Base class for all geometries represented by discrete points and elements The pointsets follow a pipe...
Definition: imstkPointSet.h:25
vtkSmartPointer< vtkDataArray > coupleVtkDataArray(std::shared_ptr< AbstractDataArray > imstkArray)
Coupling functions, these create vtk data objects that point to our data objects thus no copying is d...
std::shared_ptr< VisualModel > m_visualModel
imstk visual model (contains data (geometry) and render specification (render material)) ...
Base class for events which contain a type, priority, and data priority defaults to 0 and uses a grea...
Compound Geometry.
void vertexDataModified(Event *e)
Callback for when vertex data changes.
void processEvents() override
Update polydata source based on the mesh geometry.
std::shared_ptr< VecDataArray< double, 3 > > computeTriangleCenters(std::shared_ptr< VecDataArray< double, 3 >> verticesPtr, std::shared_ptr< VecDataArray< int, 3 >> indicesPtr)
Computes triangle centers from the current geometry.
void updateRenderProperties() override
Updates the actor and mapper properties from the currently set VisualModel.
std::shared_ptr< VecDataArray< double, 3 > > getVertexPositions(DataType type=DataType::PostTransform) const
Returns the vector of current positions of the mesh vertices.
Represents a set of triangles & vertices via an array of Vec3d double vertices & Vec3i integer indice...
void updateRenderProperties() override
Updates the actor and mapper properties from the currently set VisualModel.
void vertexDataModified(Event *e)
Callback for when vertex values are modified.
void geometryModified(Event *e)
Callback for when geometry changes.
vtkSmartPointer< vtkDoubleArray > m_mappedVertexArray
Mapped array of vertices.
Stores everything needed to invoke an event A call may not be present, in which case invoke doesn&#39;t d...