1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkFrustumSelector.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 
16 // Hide VTK_DEPRECATED_IN_9_0_0() warnings for this class.
17 #define VTK_DEPRECATION_LEVEL 0
18 
19 #include "vtkFrustumSelector.h"
20 
21 #include "vtkCell.h"
22 #include "vtkDataSet.h"
23 #include "vtkDoubleArray.h"
24 #include "vtkGenericCell.h"
25 #include "vtkIdTypeArray.h"
26 #include "vtkInformation.h"
27 #include "vtkNew.h"
28 #include "vtkPlane.h"
29 #include "vtkPlanes.h"
30 #include "vtkPoints.h"
31 #include "vtkSMPTools.h"
32 #include "vtkSelectionNode.h"
33 #include "vtkSignedCharArray.h"
34 #include "vtkVector.h"
35 #include "vtkVectorOperators.h"
36 #include "vtkVoxel.h"
37 
38 #include <vector>
39 
40 #define MAXPLANE 6
41 
42 namespace
43 {
44 //------------------------------------------------------------------------------
ComputePlane(int idx,double v0[3],double v1[3],double v2[3],vtkPoints * points,vtkDoubleArray * norms)45 void ComputePlane(
46   int idx, double v0[3], double v1[3], double v2[3], vtkPoints* points, vtkDoubleArray* norms)
47 {
48   points->SetPoint(idx, v0[0], v0[1], v0[2]);
49 
50   double e0[3];
51   e0[0] = v1[0] - v0[0];
52   e0[1] = v1[1] - v0[1];
53   e0[2] = v1[2] - v0[2];
54 
55   double e1[3];
56   e1[0] = v2[0] - v0[0];
57   e1[1] = v2[1] - v0[1];
58   e1[2] = v2[2] - v0[2];
59 
60   double n[3];
61   vtkMath::Cross(e0, e1, n);
62   vtkMath::Normalize(n);
63 
64   norms->SetTuple(idx, n);
65 }
66 
67 //------------------------------------------------------------------------------
68 //------------------------------------------------------------------------------
69 class ComputeCellsInFrustumFunctor
70 {
71 public:
ComputeCellsInFrustumFunctor(vtkPlanes * f,vtkDataSet * in,vtkSignedCharArray * array)72   ComputeCellsInFrustumFunctor(vtkPlanes* f, vtkDataSet* in, vtkSignedCharArray* array)
73     : Frustum(f)
74     , Input(in)
75     , Array(array)
76   {
77     vtkIdType i;
78     double x[3];
79 
80     // find the near and far vertices to each plane for quick in/out tests
81     for (i = 0; i < MAXPLANE; i++)
82     {
83       this->Frustum->GetNormals()->GetTuple(i, x);
84       int xside = (x[0] > 0) ? 1 : 0;
85       int yside = (x[1] > 0) ? 1 : 0;
86       int zside = (x[2] > 0) ? 1 : 0;
87       this->np_vertids[i][0] = (1 - xside) * 4 + (1 - yside) * 2 + (1 - zside);
88       this->np_vertids[i][1] = xside * 4 + yside * 2 + zside;
89     }
90   }
91 
92   //--------------------------------------------------------------------------
operator ()(vtkIdType begin,vtkIdType end)93   void operator()(vtkIdType begin, vtkIdType end)
94   {
95     double bounds[6];
96     vtkNew<vtkGenericCell> cell;
97 
98     for (vtkIdType cellId = begin; cellId < end; ++cellId)
99     {
100       Input->GetCellBounds(cellId, bounds);
101       Input->GetCell(cellId, cell);
102       int isect = this->ABoxFrustumIsect(bounds, cell);
103       if (isect == 1)
104       {
105         Array->SetValue(cellId, 1);
106       }
107       else
108       {
109         Array->SetValue(cellId, 0);
110       }
111     }
112   }
113 
114   //--------------------------------------------------------------------------
115   // Intersect the cell (with its associated bounds) with the clipping frustum.
116   // Return 1 if at least partially inside, 0 otherwise.
117   // Also return a distance to the near plane.
ABoxFrustumIsect(double * bounds,vtkCell * cell)118   int ABoxFrustumIsect(double* bounds, vtkCell* cell)
119   {
120     if (bounds[0] > bounds[1] || bounds[2] > bounds[3] || bounds[4] > bounds[5])
121     {
122       return this->IsectDegenerateCell(cell);
123     }
124 
125     // convert bounds to 8 vertices
126     double verts[8][3];
127     verts[0][0] = bounds[0];
128     verts[0][1] = bounds[2];
129     verts[0][2] = bounds[4];
130     verts[1][0] = bounds[0];
131     verts[1][1] = bounds[2];
132     verts[1][2] = bounds[5];
133     verts[2][0] = bounds[0];
134     verts[2][1] = bounds[3];
135     verts[2][2] = bounds[4];
136     verts[3][0] = bounds[0];
137     verts[3][1] = bounds[3];
138     verts[3][2] = bounds[5];
139     verts[4][0] = bounds[1];
140     verts[4][1] = bounds[2];
141     verts[4][2] = bounds[4];
142     verts[5][0] = bounds[1];
143     verts[5][1] = bounds[2];
144     verts[5][2] = bounds[5];
145     verts[6][0] = bounds[1];
146     verts[6][1] = bounds[3];
147     verts[6][2] = bounds[4];
148     verts[7][0] = bounds[1];
149     verts[7][1] = bounds[3];
150     verts[7][2] = bounds[5];
151 
152     int intersect = 0;
153 
154     // reject if any plane rejects the entire bbox
155     vtkNew<vtkPlane> plane;
156     for (int pid = 0; pid < MAXPLANE; pid++)
157     {
158       this->Frustum->GetPlane(pid, plane);
159       double dist;
160       int nvid;
161       int pvid;
162       nvid = this->np_vertids[pid][0];
163       dist = plane->EvaluateFunction(verts[nvid]);
164       if (dist > 0.0)
165       {
166         /*
167         this->NumRejects++;
168         */
169         return 0;
170       }
171       pvid = this->np_vertids[pid][1];
172       dist = plane->EvaluateFunction(verts[pvid]);
173       if (dist > 0.0)
174       {
175         intersect = 1;
176         break;
177       }
178     }
179 
180     // accept if entire bbox is inside all planes
181     if (!intersect)
182     {
183       /*
184       this->NumAccepts++;
185       */
186       return 1;
187     }
188 
189     // otherwise we have to do clipping tests to decide if actually insects
190     /*
191     this->NumIsects++;
192     */
193     vtkCell* face;
194     vtkCell* edge;
195     vtkPoints* pts = nullptr;
196     std::vector<double> vertbuffer;
197     int maxedges = 16;
198     // be ready to resize if we hit a polygon with many vertices
199     vertbuffer.resize(3 * maxedges * 3);
200     double* vlist = &vertbuffer[0 * maxedges * 3];
201     double* wvlist = &vertbuffer[1 * maxedges * 3];
202     double* ovlist = &vertbuffer[2 * maxedges * 3];
203 
204     int nfaces = cell->GetNumberOfFaces();
205     if (nfaces < 1)
206     {
207       // some 2D cells have no faces, only edges
208       int nedges = cell->GetNumberOfEdges();
209       if (nedges < 1)
210       {
211         // VTK_LINE and VTK_POLY_LINE have no "edges" -- the cells
212         // themselves are edges.  We catch them here and assemble the
213         // list of vertices by hand because the code below assumes that
214         // GetNumberOfEdges()==0 means a degenerate cell containing only
215         // points.
216         if (cell->GetCellType() == VTK_LINE)
217         {
218           nedges = 2;
219           vtkPoints* points = cell->GetPoints();
220           points->GetPoint(0, &vlist[0 * 3]);
221           points->GetPoint(1, &vlist[1 * 3]);
222         }
223         else if (cell->GetCellType() == VTK_POLY_LINE)
224         {
225           nedges = cell->GetPointIds()->GetNumberOfIds();
226           vtkPoints* points = cell->GetPoints();
227           if (nedges + 4 > maxedges)
228           {
229             maxedges = (nedges + 4) * 2;
230             vertbuffer.resize(3 * maxedges * 3);
231             vlist = &vertbuffer[0 * maxedges * 3];
232             wvlist = &vertbuffer[1 * maxedges * 3];
233             ovlist = &vertbuffer[2 * maxedges * 3];
234           }
235           for (vtkIdType i = 0; i < cell->GetNumberOfPoints(); ++i)
236           {
237             points->GetPoint(i, &vlist[i * 3]);
238           }
239         }
240         else
241         {
242           return this->IsectDegenerateCell(cell);
243         }
244       }
245       if (nedges + 4 > maxedges)
246       {
247         maxedges = (nedges + 4) * 2;
248         vertbuffer.resize(3 * maxedges * 3);
249         vlist = &vertbuffer[0 * maxedges * 3];
250         wvlist = &vertbuffer[1 * maxedges * 3];
251         ovlist = &vertbuffer[2 * maxedges * 3];
252       }
253       edge = cell->GetEdge(0);
254       if (edge)
255       {
256         pts = edge->GetPoints();
257         pts->GetPoint(0, &vlist[0 * 3]);
258         pts->GetPoint(1, &vlist[1 * 3]);
259       }
260       switch (cell->GetCellType())
261       {
262         case VTK_PIXEL:
263         {
264           edge = cell->GetEdge(2);
265           pts = edge->GetPoints();
266           pts->GetPoint(0, &vlist[3 * 3]);
267           pts->GetPoint(1, &vlist[2 * 3]);
268           break;
269         }
270         case VTK_QUAD:
271         {
272           edge = cell->GetEdge(2);
273           pts = edge->GetPoints();
274           pts->GetPoint(0, &vlist[2 * 3]);
275           pts->GetPoint(1, &vlist[3 * 3]);
276           break;
277         }
278         case VTK_TRIANGLE:
279         {
280           edge = cell->GetEdge(1);
281           pts = edge->GetPoints();
282           pts->GetPoint(1, &vlist[2 * 3]);
283           break;
284         }
285         case VTK_LINE:
286         case VTK_POLY_LINE:
287         {
288           return this->FrustumClipPolyline(nedges, vlist, bounds);
289         }
290         default:
291         {
292           for (int e = 1; e < nedges - 1; e++)
293           {
294             edge = cell->GetEdge(e);
295             pts = edge->GetPoints();
296             pts->GetPoint(1, &vlist[(e + 1) * 3]); // get second point of the edge
297           }
298           break;
299         }
300       }
301       if (this->FrustumClipPolygon(nedges, vlist, wvlist, ovlist))
302       {
303         return 1;
304       }
305     }
306     else
307     {
308 
309       // go around edges of each face and clip to planes
310       // if nothing remains at the end, then we do not intersect and reject
311       for (int f = 0; f < nfaces; f++)
312       {
313         face = cell->GetFace(f);
314 
315         int nedges = face->GetNumberOfEdges();
316         if (nedges < 1)
317         {
318           if (this->IsectDegenerateCell(face))
319           {
320             return 1;
321           }
322           continue;
323         }
324         if (nedges + 4 > maxedges)
325         {
326           maxedges = (nedges + 4) * 2;
327           vertbuffer.resize(3 * maxedges * 3);
328           vlist = &vertbuffer[0 * maxedges * 3];
329           wvlist = &vertbuffer[1 * maxedges * 3];
330           ovlist = &vertbuffer[2 * maxedges * 3];
331         }
332         edge = face->GetEdge(0);
333         pts = edge->GetPoints();
334         pts->GetPoint(0, &vlist[0 * 3]);
335         pts->GetPoint(1, &vlist[1 * 3]);
336         switch (face->GetCellType())
337         {
338           case VTK_PIXEL:
339             edge = face->GetEdge(2);
340             pts = edge->GetPoints();
341             pts->GetPoint(0, &vlist[3 * 3]);
342             pts->GetPoint(1, &vlist[2 * 3]);
343             break;
344           case VTK_QUAD:
345           {
346             edge = face->GetEdge(2);
347             pts = edge->GetPoints();
348             pts->GetPoint(0, &vlist[2 * 3]);
349             pts->GetPoint(1, &vlist[3 * 3]);
350             break;
351           }
352           case VTK_TRIANGLE:
353           {
354             edge = face->GetEdge(1);
355             pts = edge->GetPoints();
356             pts->GetPoint(1, &vlist[2 * 3]);
357             break;
358           }
359           case VTK_LINE:
360           {
361             break;
362           }
363           default:
364           {
365             for (int e = 1; e < nedges - 1; e++)
366             {
367               edge = cell->GetEdge(e);
368               pts = edge->GetPoints();
369               pts->GetPoint(1, &vlist[(e + 1) * 3]); // get second point of the edge
370             }
371             break;
372           }
373         }
374         if (this->FrustumClipPolygon(nedges, vlist, wvlist, ovlist))
375         {
376           return 1;
377         }
378       }
379     }
380 
381     return 0;
382   }
383 
384   //--------------------------------------------------------------------------
385   // handle degenerate cells by testing each point, if any in, then in
IsectDegenerateCell(vtkCell * cell)386   int IsectDegenerateCell(vtkCell* cell)
387   {
388     vtkIdType npts = cell->GetNumberOfPoints();
389     vtkPoints* pts = cell->GetPoints();
390     double x[3];
391     for (vtkIdType i = 0; i < npts; i++)
392     {
393       pts->GetPoint(i, x);
394       if (this->Frustum->EvaluateFunction(x) < 0.0)
395       {
396         return 1;
397       }
398     }
399     return 0;
400   }
401 
402   //--------------------------------------------------------------------------
403   // clips the polygon against the frustum
404   // if there is no intersection, returns 0
405   // if there is an intersection, returns 1
406   // update ovlist to contain the resulting clipped vertices
FrustumClipPolygon(int nverts,double * ivlist,double * wvlist,double * ovlist)407   int FrustumClipPolygon(int nverts, double* ivlist, double* wvlist, double* ovlist)
408   {
409     int nwverts = nverts;
410     memcpy(wvlist, ivlist, nverts * sizeof(double) * 3);
411 
412     int noverts = 0;
413     int pid;
414     for (pid = 0; pid < MAXPLANE; pid++)
415     {
416       noverts = 0;
417       this->PlaneClipPolygon(nwverts, wvlist, pid, noverts, ovlist);
418       if (noverts == 0)
419       {
420         return 0;
421       }
422       memcpy(wvlist, ovlist, noverts * sizeof(double) * 3);
423       nwverts = noverts;
424     }
425 
426     return 1;
427   }
428 
429   //--------------------------------------------------------------------------
430   // clips a polygon against the numbered plane, resulting vertices are stored
431   // in ovlist, noverts
PlaneClipPolygon(int nverts,double * ivlist,int pid,int & noverts,double * ovlist)432   void PlaneClipPolygon(int nverts, double* ivlist, int pid, int& noverts, double* ovlist)
433   {
434     int vid;
435     // run around the polygon and clip to this edge
436     for (vid = 0; vid < nverts - 1; vid++)
437     {
438       this->PlaneClipEdge(&ivlist[vid * 3], &ivlist[(vid + 1) * 3], pid, noverts, ovlist);
439     }
440     this->PlaneClipEdge(&ivlist[(nverts - 1) * 3], &ivlist[0 * 3], pid, noverts, ovlist);
441   }
442 
443   //--------------------------------------------------------------------------
444   // clips a line segment against the numbered plane.
445   // intersection point and the second vertex are added to overts if on or inside
PlaneClipEdge(double * V0,double * V1,int pid,int & noverts,double * overts)446   void PlaneClipEdge(double* V0, double* V1, int pid, int& noverts, double* overts)
447   {
448     double t = 0.0;
449     double ISECT[3];
450     double normal[3], point[3];
451     this->Frustum->GetNormals()->GetTuple(pid, normal);
452     this->Frustum->GetPoints()->GetPoint(pid, point);
453     int rc = vtkPlane::IntersectWithLine(V0, V1, normal, point, t, ISECT);
454 
455     if (rc)
456     {
457       overts[noverts * 3 + 0] = ISECT[0];
458       overts[noverts * 3 + 1] = ISECT[1];
459       overts[noverts * 3 + 2] = ISECT[2];
460       noverts++;
461     }
462 
463     vtkNew<vtkPlane> plane;
464     this->Frustum->GetPlane(pid, plane);
465 
466     if (plane->EvaluateFunction(V1) < 0.0)
467     {
468       overts[noverts * 3 + 0] = V1[0];
469       overts[noverts * 3 + 1] = V1[1];
470       overts[noverts * 3 + 2] = V1[2];
471       noverts++;
472     }
473   }
474 
475   //--------------------------------------------------------------------------
476   // Tests edge segments against the frustum.
477   // If there is no intersection, returns 0
478   // If there is an intersection, returns 1
479   // This is accomplished using Cyrus-Beck clipping.
FrustumClipPolyline(int nverts,double * ivlist,double * bounds)480   int FrustumClipPolyline(int nverts, double* ivlist, double* bounds)
481   {
482     if (nverts < 1)
483     {
484       return 0;
485     }
486     vtkVector3d p0(ivlist[0], ivlist[1], ivlist[2]);
487     if (nverts < 2)
488     {
489       return this->ComputePlaneEndpointCode(p0) == 0;
490     }
491     // Compute the L1 "diameter" of the bounding box (used to test for degeneracy)
492     // We know bounds is valid at this point, so diam >= 0
493     const double diam = bounds[1] - bounds[0] + bounds[3] - bounds[2] + bounds[5] - bounds[4];
494     const double epsilon = 1e-6 * diam;
495     const double epsilon2 = 1e-10 * diam * diam;
496     vtkVector3d normal, basePoint;
497     vtkVector3d p1;
498     bool in = false;
499     for (int ii = 1; ii < nverts; ++ii, p0 = p1)
500     {
501       p1 = vtkVector3d(ivlist[3 * ii], ivlist[3 * ii + 1], ivlist[3 * ii + 2]);
502       vtkVector3d lineVec = p1 - p0;
503       if (lineVec.SquaredNorm() < epsilon2)
504       {
505         // Skip short edges; they would make denom == 0.0 and thus have no effect.
506         continue;
507       }
508       double tmin = 0.0;
509       double tmax = 1.0;
510       bool mayOverlap = true;
511       for (int pp = 0; mayOverlap && (pp < MAXPLANE); ++pp)
512       {
513         this->Frustum->GetNormals()->GetTuple(pp, normal.GetData());
514         this->Frustum->GetPoints()->GetPoint(pp, basePoint.GetData());
515         vtkVector3d db = p0 - basePoint; // Vector from the plane's base point to p0 on the line.
516         double numer = db.Dot(normal);
517         double denom = lineVec.Dot(normal);
518         double t;
519         if (std::abs(denom) <= epsilon)
520         {
521           if (numer > 0)
522           {
523             mayOverlap = false;
524           }
525         }
526         else
527         {
528           t = -numer / denom;
529           if (denom < 0.0 && t > tmin)
530           {
531             tmin = t;
532           }
533           else if (denom > 0.0 && t < tmax)
534           {
535             tmax = t;
536           }
537         }
538       }
539       if (mayOverlap)
540       {
541         in |= (tmin <= tmax);
542         if (in)
543         {
544           break;
545         }
546       }
547     }
548     return in ? 1 : 0;
549   }
550 
ComputePlaneEndpointCode(const vtkVector3d & vertex)551   int ComputePlaneEndpointCode(const vtkVector3d& vertex)
552   {
553     int code = 0;
554     vtkVector3d normal, basePoint;
555     for (int pp = 0; pp < MAXPLANE; ++pp)
556     {
557       this->Frustum->GetNormals()->GetTuple(pp, normal.GetData());
558       this->Frustum->GetPoints()->GetPoint(pp, basePoint.GetData());
559       code |= ((vertex - basePoint).Dot(normal) >= 0.0) ? (1 << pp) : 0;
560     }
561     return code;
562   }
563 
564   vtkPlanes* Frustum;
565   vtkDataSet* Input;
566   vtkSignedCharArray* Array;
567   int np_vertids[6][2];
568 };
569 }
570 
571 //------------------------------------------------------------------------------
572 //------------------------------------------------------------------------------
573 vtkStandardNewMacro(vtkFrustumSelector);
574 
575 //------------------------------------------------------------------------------
vtkFrustumSelector(vtkPlanes * f)576 vtkFrustumSelector::vtkFrustumSelector(vtkPlanes* f)
577 {
578   this->Frustum = f;
579   if (!this->Frustum)
580   {
581     // an inside out unit cube - which selects nothing
582     double verts[32] = {
583       0.0, 0.0, 0.0, 0.0, //
584       0.0, 0.0, 1.0, 0.0, //
585       0.0, 1.0, 0.0, 0.0, //
586       0.0, 1.0, 1.0, 0.0, //
587       1.0, 0.0, 0.0, 0.0, //
588       1.0, 0.0, 1.0, 0.0, //
589       1.0, 1.0, 0.0, 0.0, //
590       1.0, 1.0, 1.0, 0.0  //
591     };
592     this->Frustum = vtkSmartPointer<vtkPlanes>::New();
593     this->CreateFrustum(verts);
594   }
595 }
596 
597 //------------------------------------------------------------------------------
598 vtkFrustumSelector::~vtkFrustumSelector() = default;
599 
600 //------------------------------------------------------------------------------
GetFrustum()601 vtkPlanes* vtkFrustumSelector::GetFrustum()
602 {
603   return this->Frustum;
604 }
605 
606 //------------------------------------------------------------------------------
SetFrustum(vtkPlanes * f)607 void vtkFrustumSelector::SetFrustum(vtkPlanes* f)
608 {
609   if (this->Frustum != f)
610   {
611     this->Frustum = f;
612     this->Modified();
613   }
614 }
615 
616 //------------------------------------------------------------------------------
617 // Overload standard modified time function. If implicit function is modified,
618 // then this object is modified as well.
GetMTime()619 vtkMTimeType vtkFrustumSelector::GetMTime()
620 {
621   vtkMTimeType mTime = this->MTime.GetMTime();
622   vtkMTimeType impFuncMTime;
623 
624   if (this->Frustum != nullptr)
625   {
626     impFuncMTime = this->Frustum->GetMTime();
627     mTime = (impFuncMTime > mTime ? impFuncMTime : mTime);
628   }
629 
630   return mTime;
631 }
632 
633 //------------------------------------------------------------------------------
CreateFrustum(double verts[32])634 void vtkFrustumSelector::CreateFrustum(double verts[32])
635 {
636   vtkNew<vtkPoints> points;
637   points->SetNumberOfPoints(6);
638 
639   vtkNew<vtkDoubleArray> norms;
640   norms->SetNumberOfComponents(3);
641   norms->SetNumberOfTuples(6);
642 
643   // left
644   ComputePlane(0, &verts[0 * 4], &verts[2 * 4], &verts[3 * 4], points, norms);
645   // right
646   ComputePlane(1, &verts[7 * 4], &verts[6 * 4], &verts[4 * 4], points, norms);
647   // bottom
648   ComputePlane(2, &verts[5 * 4], &verts[4 * 4], &verts[0 * 4], points, norms);
649   // top
650   ComputePlane(3, &verts[2 * 4], &verts[6 * 4], &verts[7 * 4], points, norms);
651   // near
652   ComputePlane(4, &verts[6 * 4], &verts[2 * 4], &verts[0 * 4], points, norms);
653   // far
654   ComputePlane(5, &verts[1 * 4], &verts[3 * 4], &verts[7 * 4], points, norms);
655 
656   this->Frustum->SetPoints(points);
657   this->Frustum->SetNormals(norms);
658 }
659 
660 //------------------------------------------------------------------------------
Initialize(vtkSelectionNode * node)661 void vtkFrustumSelector::Initialize(vtkSelectionNode* node)
662 {
663   this->Superclass::Initialize(node);
664 
665   // sanity checks
666   if (node && node->GetContentType() == vtkSelectionNode::FRUSTUM)
667   {
668     vtkDoubleArray* corners = vtkArrayDownCast<vtkDoubleArray>(node->GetSelectionList());
669     this->CreateFrustum(corners->GetPointer(0));
670   }
671   else
672   {
673     vtkErrorMacro("Wrong type of selection node used to initialize vtkFrustumSelector");
674   }
675 }
676 
677 //------------------------------------------------------------------------------
ComputeSelectedElements(vtkDataObject * input,vtkSignedCharArray * insidednessArray)678 bool vtkFrustumSelector::ComputeSelectedElements(
679   vtkDataObject* input, vtkSignedCharArray* insidednessArray)
680 {
681   vtkDataSet* inputDS = vtkDataSet::SafeDownCast(input);
682   // frustum selection only supports datasets
683   // if we don't have a selection node, the frustum is uninitialized...
684   if (!inputDS || !this->Node)
685   {
686     vtkErrorMacro("Frustum selection only supports inputs of type vtkDataSet");
687     return false;
688   }
689   auto fieldType = this->Node->GetProperties()->Get(vtkSelectionNode::FIELD_TYPE());
690   if (fieldType == vtkSelectionNode::POINT)
691   {
692     this->ComputeSelectedPoints(inputDS, insidednessArray);
693   }
694   else if (fieldType == vtkSelectionNode::CELL)
695   {
696     this->ComputeSelectedCells(inputDS, insidednessArray);
697   }
698   else
699   {
700     vtkErrorMacro("Frustum selection only supports POINT and CELL association types");
701     return false;
702   }
703   return true;
704 }
705 
706 //------------------------------------------------------------------------------
ComputeSelectedPoints(vtkDataSet * input,vtkSignedCharArray * pointSelected)707 void vtkFrustumSelector::ComputeSelectedPoints(vtkDataSet* input, vtkSignedCharArray* pointSelected)
708 {
709   vtkIdType numPts = input->GetNumberOfPoints();
710   if (numPts == 0)
711   {
712     return;
713   }
714 
715   // Hacky PrepareForMultithreadedAccess()
716   // call everything we will call on the data object on the main thread first
717   // so that it can build its caching structures
718   double xx[3];
719   input->GetPoint(0, xx);
720 
721   vtkSMPTools::For(0, numPts, [input, this, &pointSelected](vtkIdType begin, vtkIdType end) {
722     double x[3];
723     for (vtkIdType ptId = begin; ptId < end; ++ptId)
724     {
725       input->GetPoint(ptId, x);
726       if ((this->Frustum->EvaluateFunction(x)) < 0.0)
727       {
728         pointSelected->SetValue(ptId, 1);
729       }
730       else
731       {
732         pointSelected->SetValue(ptId, 0);
733       }
734     }
735   });
736 }
737 //------------------------------------------------------------------------------
ComputeSelectedCells(vtkDataSet * input,vtkSignedCharArray * cellSelected)738 void vtkFrustumSelector::ComputeSelectedCells(vtkDataSet* input, vtkSignedCharArray* cellSelected)
739 {
740   vtkIdType numCells = input->GetNumberOfCells();
741 
742   // Hacky PrepareForMultithreadedAccess()
743   // call everything we will call on the data object on the main thread first
744   // so that it can build its caching structures
745   if (numCells == 0)
746   {
747     return;
748   }
749   double bounds[6];
750   vtkNew<vtkGenericCell> cell;
751   input->GetCellBounds(0, bounds);
752   input->GetCell(0, cell);
753 
754   ComputeCellsInFrustumFunctor functor(this->Frustum, input, cellSelected);
755   vtkSMPTools::For(0, numCells, functor);
756 }
757 
PrintSelf(ostream & os,vtkIndent indent)758 void vtkFrustumSelector::PrintSelf(ostream& os, vtkIndent indent)
759 {
760   this->Superclass::PrintSelf(os, indent);
761 
762   os << indent << "Frustum: " << static_cast<void*>(this->Frustum) << "\n";
763 }
764 
OverallBoundsTest(double bounds[6])765 int vtkFrustumSelector::OverallBoundsTest(double bounds[6])
766 {
767   ComputeCellsInFrustumFunctor functor(this->Frustum, nullptr, nullptr);
768 
769   vtkNew<vtkVoxel> vox;
770   vtkPoints* p = vox->GetPoints();
771   p->SetPoint(0, bounds[0], bounds[2], bounds[4]);
772   p->SetPoint(1, bounds[1], bounds[2], bounds[4]);
773   p->SetPoint(2, bounds[0], bounds[3], bounds[4]);
774   p->SetPoint(3, bounds[1], bounds[3], bounds[4]);
775   p->SetPoint(4, bounds[0], bounds[2], bounds[5]);
776   p->SetPoint(5, bounds[1], bounds[2], bounds[5]);
777   p->SetPoint(6, bounds[0], bounds[3], bounds[5]);
778   p->SetPoint(7, bounds[1], bounds[3], bounds[5]);
779 
780   int rc;
781   rc = functor.ABoxFrustumIsect(bounds, vox);
782   return (rc > 0);
783 }
784