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