1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkClosedSurfacePointPlacer.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 #include "vtkClosedSurfacePointPlacer.h"
16 #include "vtkCamera.h"
17 #include "vtkInteractorObserver.h"
18 #include "vtkLine.h"
19 #include "vtkMath.h"
20 #include "vtkObjectFactory.h"
21 #include "vtkPlane.h"
22 #include "vtkPlaneCollection.h"
23 #include "vtkPlanes.h"
24 #include "vtkRenderer.h"
25 
26 #include <algorithm>
27 #include <vector>
28 
29 vtkStandardNewMacro(vtkClosedSurfacePointPlacer);
30 vtkCxxSetObjectMacro(vtkClosedSurfacePointPlacer, BoundingPlanes, vtkPlaneCollection);
31 
32 //------------------------------------------------------------------------------
33 // Place holder structure to find the two planes that would best cut
34 // a line with a plane. We do this freaky stuff because we cannot use
35 // absolute tolerances. Sometimes a point may be intersected by two planes
36 // when it is on a corner etc... Believe me, I found this necessary.
37 //
38 // Plane   : The plane that we found had intersected the line in question
39 // p       : The intersection point of the line and the plane.
40 // Distance: Distance of the point "p" from the object. Negative distances
41 //           mean that it is outside.
42 struct vtkClosedSurfacePointPlacerNode
43 {
44   typedef vtkClosedSurfacePointPlacerNode Self;
45   mutable vtkPlane* Plane;
46   double Distance;
47   double p[3];
SortvtkClosedSurfacePointPlacerNode48   static bool Sort(const Self& a, const Self& b) { return a.Distance > b.Distance; }
operator ==vtkClosedSurfacePointPlacerNode49   bool operator==(const Self& a) const { return a.Plane == this->Plane; }
operator !=vtkClosedSurfacePointPlacerNode50   bool operator!=(const Self& a) const { return a.Plane != this->Plane; }
vtkClosedSurfacePointPlacerNodevtkClosedSurfacePointPlacerNode51   vtkClosedSurfacePointPlacerNode()
52   {
53     Plane = nullptr;
54     Distance = VTK_DOUBLE_MIN;
55   }
56 };
57 
58 //------------------------------------------------------------------------------
vtkClosedSurfacePointPlacer()59 vtkClosedSurfacePointPlacer::vtkClosedSurfacePointPlacer()
60 {
61   this->BoundingPlanes = nullptr;
62   this->MinimumDistance = 0.0;
63   this->InnerBoundingPlanes = vtkPlaneCollection::New();
64 }
65 
66 //------------------------------------------------------------------------------
~vtkClosedSurfacePointPlacer()67 vtkClosedSurfacePointPlacer::~vtkClosedSurfacePointPlacer()
68 {
69   this->RemoveAllBoundingPlanes();
70   if (this->BoundingPlanes)
71   {
72     this->BoundingPlanes->UnRegister(this);
73   }
74   this->InnerBoundingPlanes->Delete();
75 }
76 
77 //------------------------------------------------------------------------------
AddBoundingPlane(vtkPlane * plane)78 void vtkClosedSurfacePointPlacer::AddBoundingPlane(vtkPlane* plane)
79 {
80   if (this->BoundingPlanes == nullptr)
81   {
82     this->BoundingPlanes = vtkPlaneCollection::New();
83     this->BoundingPlanes->Register(this);
84     this->BoundingPlanes->Delete();
85   }
86 
87   this->BoundingPlanes->AddItem(plane);
88 }
89 
90 //------------------------------------------------------------------------------
RemoveBoundingPlane(vtkPlane * plane)91 void vtkClosedSurfacePointPlacer::RemoveBoundingPlane(vtkPlane* plane)
92 {
93   if (this->BoundingPlanes)
94   {
95     this->BoundingPlanes->RemoveItem(plane);
96   }
97 }
98 
99 //------------------------------------------------------------------------------
RemoveAllBoundingPlanes()100 void vtkClosedSurfacePointPlacer::RemoveAllBoundingPlanes()
101 {
102   if (this->BoundingPlanes)
103   {
104     this->BoundingPlanes->RemoveAllItems();
105     this->BoundingPlanes->Delete();
106     this->BoundingPlanes = nullptr;
107   }
108 }
109 //------------------------------------------------------------------------------
110 
SetBoundingPlanes(vtkPlanes * planes)111 void vtkClosedSurfacePointPlacer::SetBoundingPlanes(vtkPlanes* planes)
112 {
113   if (!planes)
114   {
115     return;
116   }
117 
118   vtkPlane* plane;
119   int numPlanes = planes->GetNumberOfPlanes();
120 
121   this->RemoveAllBoundingPlanes();
122   for (int i = 0; i < numPlanes; i++)
123   {
124     plane = vtkPlane::New();
125     planes->GetPlane(i, plane);
126     this->AddBoundingPlane(plane);
127     plane->Delete();
128   }
129 }
130 
131 //------------------------------------------------------------------------------
BuildPlanes()132 void vtkClosedSurfacePointPlacer::BuildPlanes()
133 {
134   if (this->InnerBoundingPlanes->GetMTime() > this->GetMTime() &&
135     this->InnerBoundingPlanes->GetMTime() > this->BoundingPlanes->GetMTime())
136   {
137     return;
138   }
139 
140   // Need to build planes.. Bring them all in front by MinimumDistance.
141   // Find the Inner bounding planes.
142 
143   this->InnerBoundingPlanes->RemoveAllItems();
144 
145   double normal[3], origin[3];
146   vtkPlane* p;
147   for (this->BoundingPlanes->InitTraversal(); (p = this->BoundingPlanes->GetNextItem());)
148   {
149     p->GetNormal(normal);
150     p->GetOrigin(origin);
151     for (int i = 0; i < 3; i++)
152     {
153       origin[i] += this->MinimumDistance * normal[i];
154     }
155 
156     vtkPlane* plane = vtkPlane::New();
157     plane->SetOrigin(origin);
158     plane->SetNormal(normal);
159     this->InnerBoundingPlanes->AddItem(plane);
160     plane->Delete();
161   }
162 }
163 
164 //------------------------------------------------------------------------------
165 // Given a renderer, a display position and a reference position, "worldPos"
166 // is calculated as :
167 //   Consider the line "L" that passes through the supplied "displayPos" and
168 // is parallel to the direction of projection of the camera. Clip this line
169 // segment with the parallelopiped, let's call it "L_segment". The computed
170 // world position, "worldPos" will be the point on "L_segment" that is closest
171 // to refWorldPos.
ComputeWorldPosition(vtkRenderer * ren,double displayPos[2],double refWorldPos[3],double worldPos[3],double * vtkNotUsed (worldOrient))172 int vtkClosedSurfacePointPlacer::ComputeWorldPosition(vtkRenderer* ren, double displayPos[2],
173   double refWorldPos[3], double worldPos[3], double* vtkNotUsed(worldOrient))
174 {
175   this->BuildPlanes();
176 
177   if (!this->BoundingPlanes)
178   {
179     return 0;
180   }
181 
182   double directionOfProjection[3], t, d[3], currentWorldPos[4], ls[2][3], fp[4];
183 
184   vtkInteractorObserver::ComputeWorldToDisplay(
185     ren, refWorldPos[0], refWorldPos[1], refWorldPos[2], fp);
186 
187   ren->GetActiveCamera()->GetDirectionOfProjection(directionOfProjection);
188   vtkInteractorObserver::ComputeDisplayToWorld(
189     ren, displayPos[0], displayPos[1], fp[2], currentWorldPos);
190 
191   // The line "L" defined by two points, l0 and l1. The line-segment
192   // end-points will be defined by points ls[2][3].
193   double l0[3] = { currentWorldPos[0] - directionOfProjection[0],
194     currentWorldPos[1] - directionOfProjection[1], currentWorldPos[2] - directionOfProjection[2] };
195   double l1[3] = { currentWorldPos[0] + directionOfProjection[0],
196     currentWorldPos[1] + directionOfProjection[1], currentWorldPos[2] + directionOfProjection[2] };
197 
198   // Traverse all the planes to clip the line.
199 
200   vtkPlaneCollection* pc = this->InnerBoundingPlanes;
201 
202   // Stores candidate intersections with the parallelopiped. This was found
203   // necessary instead of a simple two point intersection test because of
204   // tolerances in vtkPlane::EvaluatePosition when the handle was very close
205   // to an edge.
206   std::vector<vtkClosedSurfacePointPlacerNode> intersections;
207 
208   const int nPlanes = pc->GetNumberOfItems();
209 
210   // Loop over each plane.
211   for (int n = 0; n < nPlanes; n++)
212   {
213     vtkPlane* plane = static_cast<vtkPlane*>(pc->GetItemAsObject(n));
214     vtkClosedSurfacePointPlacerNode node;
215 
216     vtkPlane::IntersectWithLine(l0, l1, plane->GetNormal(), plane->GetOrigin(), t, node.p);
217 
218     // The IF below insures that the line and the plane aren't parallel.
219     if (t != VTK_DOUBLE_MAX)
220     {
221       node.Plane = plane;
222       node.Distance =
223         vtkClosedSurfacePointPlacer::GetDistanceFromObject(node.p, this->InnerBoundingPlanes, d);
224       intersections.push_back(node);
225       vtkDebugMacro(<< "We aren't parallel to plane with normal: (" << plane->GetNormal()[0] << ","
226                     << plane->GetNormal()[1] << "," << plane->GetNormal()[2] << ")");
227       vtkDebugMacro(<< "Size of inersections = " << intersections.size()
228                     << " Distance: " << node.Distance << " Plane: " << plane);
229     }
230   }
231 
232   std::sort(intersections.begin(), intersections.end(), vtkClosedSurfacePointPlacerNode::Sort);
233 
234   // Now pick the top two candidates, insuring that the line at least intersects
235   // with the object. If we have fewer than 2 in the queue, or if the
236   // top candidate is outsude, we have failed to intersect the object.
237 
238   std::vector<vtkClosedSurfacePointPlacerNode>::const_iterator it = intersections.begin();
239   if (intersections.size() < 2 || it->Distance < (-1.0 * this->WorldTolerance) ||
240     (++it)->Distance < (-1.0 * this->WorldTolerance))
241   {
242     // The display point points to a location outside the object. Just
243     // return 0. In actuality, I'd like to return the closest point in the
244     // object. For this I require an algorithm that can, given a point "p" and
245     // an object "O", defined by a set of bounding planes, find the point on
246     // "O" that is closest to "p"
247 
248     return 0;
249   }
250 
251   it = intersections.begin();
252   for (int i = 0; i < 2; i++, ++it)
253   {
254     ls[i][0] = it->p[0];
255     ls[i][1] = it->p[1];
256     ls[i][2] = it->p[2];
257   }
258 
259   vtkLine::DistanceToLine(refWorldPos, ls[0], ls[1], t, worldPos);
260   t = (t < 0.0 ? 0.0 : (t > 1.0 ? 1.0 : t));
261 
262   // the point "worldPos", now lies within the object and on the line from
263   // the eye along the direction of projection.
264 
265   worldPos[0] = ls[0][0] * (1.0 - t) + ls[1][0] * t;
266   worldPos[1] = ls[0][1] * (1.0 - t) + ls[1][1] * t;
267   worldPos[2] = ls[0][2] * (1.0 - t) + ls[1][2] * t;
268 
269   vtkDebugMacro(<< "Reference Pos: (" << refWorldPos[0] << "," << refWorldPos[1] << ","
270                 << refWorldPos[2] << ")  Line segment from "
271                 << "the eye along the direction of projection, clipped by the object [(" << ls[0][0]
272                 << "," << ls[0][1] << "," << ls[0][2] << ") - (" << ls[1][0] << "," << ls[1][1]
273                 << "," << ls[1][2] << ")] Computed position (that is "
274                 << "the closest point on this segment to ReferencePos: (" << worldPos[0] << ","
275                 << worldPos[1] << "," << worldPos[2] << ")");
276 
277   return 1;
278 }
279 
280 //------------------------------------------------------------------------------
ComputeWorldPosition(vtkRenderer *,double vtkNotUsed (displayPos)[2],double vtkNotUsed (worldPos)[3],double vtkNotUsed (worldOrient)[9])281 int vtkClosedSurfacePointPlacer ::ComputeWorldPosition(vtkRenderer*,
282   double vtkNotUsed(displayPos)[2], double vtkNotUsed(worldPos)[3],
283   double vtkNotUsed(worldOrient)[9])
284 {
285   vtkErrorMacro(<< "This placer needs a reference world position.");
286   return 0;
287 }
288 
289 //------------------------------------------------------------------------------
ValidateWorldPosition(double worldPos[3],double * vtkNotUsed (worldOrient))290 int vtkClosedSurfacePointPlacer::ValidateWorldPosition(
291   double worldPos[3], double* vtkNotUsed(worldOrient))
292 {
293   return this->ValidateWorldPosition(worldPos);
294 }
295 
296 //------------------------------------------------------------------------------
ValidateWorldPosition(double worldPos[3])297 int vtkClosedSurfacePointPlacer::ValidateWorldPosition(double worldPos[3])
298 {
299   this->BuildPlanes();
300 
301   // Now check against the bounding planes
302   if (this->InnerBoundingPlanes)
303   {
304     vtkPlane* p;
305 
306     this->InnerBoundingPlanes->InitTraversal();
307 
308     while ((p = this->InnerBoundingPlanes->GetNextItem()))
309     {
310       if (p->EvaluateFunction(worldPos) < this->WorldTolerance)
311       {
312         return 0;
313       }
314     }
315   }
316   return 1;
317 }
318 
319 //------------------------------------------------------------------------------
320 // Calculate the distance of a point from the Object. Negative
321 // values imply that the point is outside. Positive values imply that it is
322 // inside. The closest point to the object is returned in closestPt.
GetDistanceFromObject(double pos[3],vtkPlaneCollection * pc,double closestPt[3])323 double vtkClosedSurfacePointPlacer ::GetDistanceFromObject(
324   double pos[3], vtkPlaneCollection* pc, double closestPt[3])
325 {
326   vtkPlane* minPlane = nullptr;
327   double minD = VTK_DOUBLE_MAX;
328 
329   pc->InitTraversal();
330   while (vtkPlane* p = pc->GetNextItem())
331   {
332     const double d = p->EvaluateFunction(pos);
333     if (d < minD)
334     {
335       minD = d;
336       minPlane = p;
337     }
338   }
339 
340   vtkPlane::ProjectPoint(pos, minPlane->GetOrigin(), minPlane->GetNormal(), closestPt);
341   return minD;
342 }
343 
344 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)345 void vtkClosedSurfacePointPlacer::PrintSelf(ostream& os, vtkIndent indent)
346 {
347   this->Superclass::PrintSelf(os, indent);
348 
349   os << indent << "Bounding Planes:\n";
350   if (this->BoundingPlanes)
351   {
352     this->BoundingPlanes->PrintSelf(os, indent.GetNextIndent());
353   }
354   else
355   {
356     os << " (none)\n";
357   }
358 
359   os << indent << "Minimum Distance: " << this->MinimumDistance << "\n";
360 }
361