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