1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkHardwareSelector.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 "vtkHardwareSelector.h"
16 
17 #include "vtkCommand.h"
18 #include "vtkDataObject.h"
19 #include "vtkDataSetAttributes.h"
20 #include "vtkIdTypeArray.h"
21 #include "vtkInformation.h"
22 #include "vtkObjectFactory.h"
23 #include "vtkProp.h"
24 #include "vtkRenderWindow.h"
25 #include "vtkRenderer.h"
26 #include "vtkSelection.h"
27 #include "vtkSelectionNode.h"
28 #include "vtkSmartPointer.h"
29 #include "vtkStructuredExtent.h"
30 
31 #include <algorithm>
32 #include <cassert>
33 #include <map>
34 #include <set>
35 
36 #define ID_OFFSET 1
37 
38 //------------------------------------------------------------------------------
39 namespace
40 {
41 class PixelInformationComparator
42 {
43 public:
operator ()(const vtkHardwareSelector::PixelInformation & a,const vtkHardwareSelector::PixelInformation & b) const44   bool operator()(const vtkHardwareSelector::PixelInformation& a,
45     const vtkHardwareSelector::PixelInformation& b) const
46   {
47     if (a.Valid != b.Valid)
48     {
49       return a.Valid < b.Valid;
50     }
51     if (a.ProcessID != b.ProcessID)
52     {
53       return a.ProcessID < b.ProcessID;
54     }
55     if (a.Prop != b.Prop)
56     {
57       return a.Prop < b.Prop;
58     }
59     if (a.PropID != b.PropID)
60     {
61       return a.PropID < b.PropID;
62     }
63     return a.CompositeID < b.CompositeID;
64 
65     // We don't consider AttributeID in this comparison
66   }
67 };
68 }
69 
70 class vtkHardwareSelector::vtkInternals
71 {
72 public:
73   // Ids for props that were hit.
74   std::set<int> HitProps;
75   std::map<int, vtkSmartPointer<vtkProp>> Props;
76   std::map<int, std::vector<unsigned int>> PropPixels;
77   std::map<int, double> ZValues;
78 
79   // state that's managed through the renderer
80   double OriginalBackground[3];
81   bool OriginalGradient;
82 
83   typedef std::map<PixelInformation, std::set<vtkIdType>, PixelInformationComparator>
84     MapOfAttributeIds;
85 
86   typedef std::map<PixelInformation, vtkIdType, PixelInformationComparator> PixelCountType;
87 
88   //-----------------------------------------------------------------------------
ConvertSelection(int fieldassociation,const MapOfAttributeIds & dataMap,const PixelCountType & pixelCounts)89   vtkSelection* ConvertSelection(
90     int fieldassociation, const MapOfAttributeIds& dataMap, const PixelCountType& pixelCounts)
91   {
92     vtkSelection* sel = vtkSelection::New();
93 
94     MapOfAttributeIds::const_iterator iter;
95     for (iter = dataMap.begin(); iter != dataMap.end(); ++iter)
96     {
97       const PixelInformation& key = iter->first;
98       const std::set<vtkIdType>& id_values = iter->second;
99       vtkSelectionNode* child = vtkSelectionNode::New();
100       child->SetContentType(vtkSelectionNode::INDICES);
101       switch (fieldassociation)
102       {
103         case vtkDataObject::FIELD_ASSOCIATION_CELLS:
104           child->SetFieldType(vtkSelectionNode::CELL);
105           break;
106 
107         case vtkDataObject::FIELD_ASSOCIATION_POINTS:
108           child->SetFieldType(vtkSelectionNode::POINT);
109           break;
110       }
111       child->GetProperties()->Set(vtkSelectionNode::PROP_ID(), key.PropID);
112       child->GetProperties()->Set(vtkSelectionNode::PROP(), key.Prop);
113 
114       if (this->ZValues.find(key.PropID) != this->ZValues.end())
115       {
116         child->GetProperties()->Set(vtkSelectionNode::ZBUFFER_VALUE(), this->ZValues[key.PropID]);
117       }
118 
119       PixelCountType::const_iterator pit = pixelCounts.find(key);
120       child->GetProperties()->Set(vtkSelectionNode::PIXEL_COUNT(), pit->second);
121       if (key.ProcessID >= 0)
122       {
123         child->GetProperties()->Set(vtkSelectionNode::PROCESS_ID(), key.ProcessID);
124       }
125 
126       child->GetProperties()->Set(vtkSelectionNode::COMPOSITE_INDEX(), key.CompositeID);
127 
128       vtkIdTypeArray* ids = vtkIdTypeArray::New();
129       ids->SetName("SelectedIds");
130       ids->SetNumberOfComponents(1);
131       ids->SetNumberOfTuples(static_cast<vtkIdType>(iter->second.size()));
132       vtkIdType* ptr = ids->GetPointer(0);
133       std::set<vtkIdType>::const_iterator idIter;
134       vtkIdType cc = 0;
135       for (idIter = id_values.begin(); idIter != id_values.end(); ++idIter, ++cc)
136       {
137         ptr[cc] = *idIter;
138       }
139       child->SetSelectionList(ids);
140       ids->FastDelete();
141       sel->AddNode(child);
142       child->FastDelete();
143     }
144 
145     return sel;
146   }
147 
148   //-----------------------------------------------------------------------------
PixelInsidePolygon(float x,float y,int * polygonPoints,vtkIdType count)149   bool PixelInsidePolygon(float x, float y, int* polygonPoints, vtkIdType count)
150   {
151     // http://en.wikipedia.org/wiki/Point_in_polygon
152     // RayCasting method shooting the ray along the x axis, using float
153     bool inside = false;
154     float xintersection;
155     for (vtkIdType i = 0; i < count; i += 2)
156     {
157       float p1X = polygonPoints[i];
158       float p1Y = polygonPoints[i + 1];
159       float p2X = polygonPoints[(i + 2) % count];
160       float p2Y = polygonPoints[(i + 3) % count];
161 
162       if (y > std::min(p1Y, p2Y) && y <= std::max(p1Y, p2Y) && p1Y != p2Y)
163       {
164         if (x <= std::max(p1X, p2X))
165         {
166           xintersection = (y - p1Y) * (p2X - p1X) / (p2Y - p1Y) + p1X;
167           if (p1X == p2X || x <= xintersection)
168           {
169             // each time intersect, toggle inside
170             inside = !inside;
171           }
172         }
173       }
174     }
175 
176     return inside;
177   }
178 };
179 
180 //------------------------------------------------------------------------------
181 vtkAbstractObjectFactoryNewMacro(vtkHardwareSelector);
182 
183 //------------------------------------------------------------------------------
184 vtkCxxSetObjectMacro(vtkHardwareSelector, Renderer, vtkRenderer);
185 
186 //------------------------------------------------------------------------------
vtkHardwareSelector()187 vtkHardwareSelector::vtkHardwareSelector()
188 {
189   this->Internals = new vtkInternals();
190   this->Renderer = nullptr;
191   this->Area[0] = this->Area[1] = this->Area[2] = this->Area[3] = 0;
192   this->FieldAssociation = vtkDataObject::FIELD_ASSOCIATION_CELLS;
193   this->MaximumPointId = 0;
194   this->MaximumCellId = 0;
195   for (int cc = 0; cc < 10; cc++)
196   {
197     this->RawPixBuffer[cc] = nullptr;
198     this->PixBuffer[cc] = nullptr;
199   }
200   this->CurrentPass = -1;
201   this->ProcessID = -1;
202   this->PropColorValue[0] = this->PropColorValue[1] = this->PropColorValue[2] = 0;
203   this->InPropRender = 0;
204   this->UseProcessIdFromData = false;
205   this->ActorPassOnly = false;
206   this->CaptureZValues = false;
207 }
208 
209 //------------------------------------------------------------------------------
~vtkHardwareSelector()210 vtkHardwareSelector::~vtkHardwareSelector()
211 {
212   this->SetRenderer(nullptr);
213   this->ReleasePixBuffers();
214   delete this->Internals;
215 }
216 
217 //------------------------------------------------------------------------------
ReleasePixBuffers()218 void vtkHardwareSelector::ReleasePixBuffers()
219 {
220   for (int cc = 0; cc < 10; cc++)
221   {
222     delete[] this->PixBuffer[cc];
223     this->PixBuffer[cc] = nullptr;
224     delete[] this->RawPixBuffer[cc];
225     this->RawPixBuffer[cc] = nullptr;
226   }
227   // this->Internals->Props.clear();
228 }
229 
230 //------------------------------------------------------------------------------
BeginSelection()231 void vtkHardwareSelector::BeginSelection()
232 {
233   this->MaximumPointId = 0;
234   this->MaximumCellId = 0;
235   this->Renderer->SetSelector(this);
236   this->Internals->HitProps.clear();
237   this->Internals->ZValues.clear();
238   this->Internals->Props.clear();
239   this->Internals->PropPixels.clear();
240   this->ReleasePixBuffers();
241 }
242 
243 //------------------------------------------------------------------------------
EndSelection()244 void vtkHardwareSelector::EndSelection()
245 {
246   this->Internals->HitProps.clear();
247   this->Renderer->SetSelector(nullptr);
248 }
249 
250 //------------------------------------------------------------------------------
Select()251 vtkSelection* vtkHardwareSelector::Select()
252 {
253   vtkSelection* sel = nullptr;
254   if (this->CaptureBuffers())
255   {
256     sel = this->GenerateSelection();
257     this->ReleasePixBuffers();
258   }
259   return sel;
260 }
261 
262 //------------------------------------------------------------------------------
CaptureBuffers()263 bool vtkHardwareSelector::CaptureBuffers()
264 {
265   if (!this->Renderer)
266   {
267     vtkErrorMacro("Renderer must be set before calling Select.");
268     return false;
269   }
270 
271   vtkRenderWindow* rwin = this->Renderer->GetRenderWindow();
272   rwin->MakeCurrent();
273 
274   int rgba[4];
275   rwin->GetColorBufferSizes(rgba);
276   if (rgba[0] < 8 || rgba[1] < 8 || rgba[2] < 8)
277   {
278     vtkErrorMacro("Color buffer depth must be at least 8 bit. "
279                   "Currently: "
280       << rgba[0] << ", " << rgba[1] << ", " << rgba[2]);
281     return false;
282   }
283   this->InvokeEvent(vtkCommand::StartEvent);
284 
285   rwin->SwapBuffersOff();
286 
287   // Initialize renderer for selection.
288   // change the renderer's background to black, which will indicate a miss
289   this->Renderer->GetBackground(this->Internals->OriginalBackground);
290   this->Renderer->SetBackground(0.0, 0.0, 0.0);
291   this->Internals->OriginalGradient = this->Renderer->GetGradientBackground();
292   this->Renderer->GradientBackgroundOff();
293 
294   int preserveDepth = this->Renderer->GetPreserveDepthBuffer();
295   int preserveColor = this->Renderer->GetPreserveColorBuffer();
296   this->Renderer->SetPreserveDepthBuffer(0);
297   this->Renderer->SetPreserveColorBuffer(0);
298 
299   this->BeginSelection();
300 
301   // maybe the second iteration could just call process pixel buffers
302   // I think that is all that is needed instead of the full PreCapture, Render, PostCapture.
303   // Normally render is what calls ProcessPixelBuffers
304 
305   for (this->Iteration = 0; this->Iteration < 2; this->Iteration++)
306   {
307     for (this->CurrentPass = MIN_KNOWN_PASS; this->CurrentPass <= MAX_KNOWN_PASS;
308          this->CurrentPass++)
309     {
310       if (!this->PassRequired(this->CurrentPass))
311       {
312         continue;
313       }
314 
315       this->PreCapturePass(this->CurrentPass);
316       rwin->Render();
317       this->PostCapturePass(this->CurrentPass);
318     }
319   }
320   this->EndSelection();
321 
322   this->Renderer->SetPreserveDepthBuffer(preserveDepth);
323   this->Renderer->SetPreserveColorBuffer(preserveColor);
324 
325   // restore original background
326   this->Renderer->SetBackground(this->Internals->OriginalBackground);
327   this->Renderer->SetGradientBackground(this->Internals->OriginalGradient);
328   this->Renderer->GetRenderWindow()->SwapBuffersOn();
329   this->InvokeEvent(vtkCommand::EndEvent);
330   return true;
331 }
332 
SetPropColorValue(vtkIdType val)333 void vtkHardwareSelector::SetPropColorValue(vtkIdType val)
334 {
335   float color[3];
336   vtkHardwareSelector::Convert(val, color);
337   this->SetPropColorValue(color);
338 }
339 
HasHighPointIds()340 bool vtkHardwareSelector::HasHighPointIds()
341 {
342   return this->MaximumPointId >= 0xffffff;
343 }
344 
HasHighCellIds()345 bool vtkHardwareSelector::HasHighCellIds()
346 {
347   return this->MaximumCellId >= 0xffffff;
348 }
349 
350 //------------------------------------------------------------------------------
PassRequired(int pass)351 bool vtkHardwareSelector::PassRequired(int pass)
352 {
353   if (this->ActorPassOnly)
354   {
355     return (pass == ACTOR_PASS);
356   }
357 
358   switch (pass)
359   {
360     case ACTOR_PASS:
361       // only on the first iteration
362       return (this->Iteration == 0);
363 
364     case PROCESS_PASS:
365       // skip process pass if pid < 0 or not the first pass
366       return (this->ProcessID >= 0 && this->Iteration == 0);
367 
368     case POINT_ID_LOW24:
369       return (this->MaximumPointId >= 0xffffff || this->Iteration == 0);
370 
371     case POINT_ID_HIGH24:
372       return (this->MaximumPointId >= 0xffffff && this->Iteration == 0);
373 
374     case CELL_ID_LOW24:
375       return (this->MaximumCellId >= 0xffffff || this->Iteration == 0);
376 
377     case CELL_ID_HIGH24:
378       return (this->MaximumCellId >= 0xffffff && this->Iteration == 0);
379   }
380 
381   return true;
382 }
383 
384 //------------------------------------------------------------------------------
SavePixelBuffer(int passNo)385 void vtkHardwareSelector::SavePixelBuffer(int passNo)
386 {
387   delete[] this->PixBuffer[passNo];
388   this->PixBuffer[passNo] =
389     this->Renderer->GetRenderWindow()->GetPixelData(this->Area[0], this->Area[1], this->Area[2],
390       this->Area[3], (this->Renderer->GetRenderWindow()->GetSwapBuffers() == 1) ? 1 : 0);
391 
392   // we save the raw buffers the first time we see them
393   if (!this->RawPixBuffer[passNo])
394   {
395     size_t numpix = (this->Area[2] - this->Area[0] + 1) * (this->Area[3] - this->Area[1] + 1) * 3;
396     this->RawPixBuffer[passNo] = new unsigned char[numpix];
397     memcpy(this->RawPixBuffer[passNo], this->PixBuffer[passNo], numpix);
398   }
399 }
400 
401 //------------------------------------------------------------------------------
ProcessPixelBuffers()402 void vtkHardwareSelector::ProcessPixelBuffers()
403 {
404   if (this->CurrentPass == ACTOR_PASS)
405   {
406     this->BuildPropHitList(this->RawPixBuffer[this->CurrentPass]);
407   }
408 
409   for (int ai : this->Internals->HitProps)
410   {
411     vtkProp* prop = this->GetPropFromID(ai);
412     if (prop)
413     {
414       prop->ProcessSelectorPixelBuffers(this, this->Internals->PropPixels[ai]);
415     }
416   }
417 }
418 
419 //------------------------------------------------------------------------------
420 // Also store the prop zvalues here as we traverse the images
BuildPropHitList(unsigned char * pixelbuffer)421 void vtkHardwareSelector::BuildPropHitList(unsigned char* pixelbuffer)
422 {
423   // grab the zbuffer if requested
424   float* depthBuffer = nullptr;
425   if (this->CaptureZValues)
426   {
427     depthBuffer = this->Renderer->GetRenderWindow()->GetZbufferData(
428       this->Area[0], this->Area[1], this->Area[2], this->Area[3]);
429   }
430 
431   unsigned int offset = 0;
432   for (int yy = 0; yy <= static_cast<int>(this->Area[3] - this->Area[1]); yy++)
433   {
434     for (int xx = 0; xx <= static_cast<int>(this->Area[2] - this->Area[0]); xx++)
435     {
436       int val = this->Convert(xx, yy, pixelbuffer);
437       if (val > 0)
438       {
439         val -= ID_OFFSET;
440         if (this->Internals->HitProps.find(val) == this->Internals->HitProps.end())
441         {
442           this->Internals->HitProps.insert(val);
443           this->Internals->ZValues[val] = 1.0;
444         }
445         if (depthBuffer)
446         {
447           if (depthBuffer[offset] < this->Internals->ZValues[val])
448           {
449             this->Internals->ZValues[val] = depthBuffer[offset];
450           }
451         }
452         this->Internals->PropPixels[val].push_back(offset * 3);
453       }
454       offset++;
455     }
456   }
457 
458   delete[] depthBuffer;
459 }
460 
GetZValue(int val)461 double vtkHardwareSelector::GetZValue(int val)
462 {
463   auto zitr = this->Internals->ZValues.find(val);
464   if (zitr != this->Internals->ZValues.end())
465   {
466     return zitr->second;
467   }
468   return 1.0;
469 }
470 
471 //------------------------------------------------------------------------------
BeginRenderProp()472 void vtkHardwareSelector::BeginRenderProp()
473 {
474   this->InPropRender++;
475   if (this->InPropRender != 1)
476   {
477     return;
478   }
479 
480   // device specific prep
481   vtkRenderWindow* renWin = this->Renderer->GetRenderWindow();
482   this->BeginRenderProp(renWin);
483 }
484 
485 //------------------------------------------------------------------------------
EndRenderProp()486 void vtkHardwareSelector::EndRenderProp()
487 {
488   if (this->InPropRender)
489   {
490     this->InPropRender--;
491 
492     if (this->InPropRender != 0)
493     {
494       return;
495     }
496 
497     // device specific cleanup
498     vtkRenderWindow* renWin = this->Renderer->GetRenderWindow();
499     this->EndRenderProp(renWin);
500   }
501 }
502 
503 //------------------------------------------------------------------------------
RenderCompositeIndex(unsigned int index)504 void vtkHardwareSelector::RenderCompositeIndex(unsigned int index)
505 {
506 
507   if (index > 0xffffff)
508   {
509     vtkErrorMacro("Indices > 0xffffff are not supported.");
510     return;
511   }
512 }
513 
514 //------------------------------------------------------------------------------
UpdateMaximumPointId(vtkIdType attribid)515 void vtkHardwareSelector::UpdateMaximumPointId(vtkIdType attribid)
516 {
517   if (attribid < 0)
518   {
519     // negative attribid is valid. It happens when rendering higher order
520     // elements where new points are added for rendering smooth surfaces.
521     return;
522   }
523 
524   this->MaximumPointId = (attribid > this->MaximumPointId) ? attribid : this->MaximumPointId;
525 }
526 
527 //------------------------------------------------------------------------------
UpdateMaximumCellId(vtkIdType attribid)528 void vtkHardwareSelector::UpdateMaximumCellId(vtkIdType attribid)
529 {
530   if (attribid < 0)
531   {
532     // negative attribid is valid. It happens when rendering higher order
533     // elements where new points are added for rendering smooth surfaces.
534     return;
535   }
536 
537   this->MaximumCellId = (attribid > this->MaximumCellId) ? attribid : this->MaximumCellId;
538 }
539 
540 //------------------------------------------------------------------------------
RenderProcessId(unsigned int processid)541 void vtkHardwareSelector::RenderProcessId(unsigned int processid)
542 {
543   if (this->CurrentPass == PROCESS_PASS && this->UseProcessIdFromData)
544   {
545     if (processid >= 0xffffff)
546     {
547       vtkErrorMacro("Invalid id: " << processid);
548       return;
549     }
550   }
551 }
552 
553 //------------------------------------------------------------------------------
Render(vtkRenderer * renderer,vtkProp ** propArray,int propArrayCount)554 int vtkHardwareSelector::Render(vtkRenderer* renderer, vtkProp** propArray, int propArrayCount)
555 {
556   if (this->Renderer != renderer)
557   {
558     vtkErrorMacro("Usage error.");
559     return 0;
560   }
561 
562   int propsRendered = 0;
563   // loop through props and give them a chance to
564   // render themselves as opaque geometry
565   for (int i = 0; i < propArrayCount; i++)
566   {
567     // all props in propArray are already visible, hence no need to check again
568     // (vtkRenderer ensures that).
569     if (!propArray[i]->GetPickable())
570     {
571       continue;
572     }
573     this->PropID = this->GetPropID(i, propArray[i]);
574     this->Internals->Props[this->PropID] = propArray[i];
575     if (this->IsPropHit(this->PropID))
576     {
577       propsRendered += propArray[i]->RenderOpaqueGeometry(renderer);
578     }
579   }
580 
581   // Render props as volumetric data.
582   for (int i = 0; i < propArrayCount; i++)
583   {
584     if (!propArray[i]->GetPickable())
585     {
586       continue;
587     }
588     this->PropID = this->GetPropID(i, propArray[i]);
589     this->Internals->Props[this->PropID] = propArray[i];
590     if (this->IsPropHit(this->PropID))
591     {
592       propsRendered += propArray[i]->RenderVolumetricGeometry(renderer);
593     }
594   }
595 
596   // loop through props and give them a chance to render themselves as
597   // overlay geometry. This allows the overlay geometry
598   // also being selected. The props in overlay can use Pickable or
599   // SupportsSelection variables to decide whether to be included in this pass.
600   for (int i = 0; i < propArrayCount; i++)
601   {
602     if (!propArray[i]->GetPickable())
603     {
604       continue;
605     }
606     this->PropID = this->GetPropID(i, propArray[i]);
607     this->Internals->Props[this->PropID] = propArray[i];
608     if (this->IsPropHit(this->PropID))
609     {
610       propsRendered += propArray[i]->RenderOverlay(renderer);
611     }
612   }
613 
614   // loop over hit props and give them a chance to modify the
615   // buffer
616 
617   this->SavePixelBuffer(this->CurrentPass);
618   this->ProcessPixelBuffers();
619 
620   return propsRendered;
621 }
622 
623 //------------------------------------------------------------------------------
GetPropFromID(int id)624 vtkProp* vtkHardwareSelector::GetPropFromID(int id)
625 {
626   std::map<int, vtkSmartPointer<vtkProp>>::iterator iter = this->Internals->Props.find(id);
627   if (iter != this->Internals->Props.end())
628   {
629     return iter->second;
630   }
631   return nullptr;
632 }
633 
634 //------------------------------------------------------------------------------
PassTypeToString(PassTypes type)635 std::string vtkHardwareSelector::PassTypeToString(PassTypes type)
636 {
637   switch (type)
638   {
639     case vtkHardwareSelector::PROCESS_PASS:
640       return "PROCESS_PASS";
641     case vtkHardwareSelector::ACTOR_PASS:
642       return "ACTOR_PASS";
643     case vtkHardwareSelector::COMPOSITE_INDEX_PASS:
644       return "COMPOSITE_INDEX_PASS";
645     case vtkHardwareSelector::POINT_ID_LOW24:
646       return "POINT_ID_LOW24_PASS";
647     case vtkHardwareSelector::POINT_ID_HIGH24:
648       return "POINT_ID_HIGH24_PASS";
649     case vtkHardwareSelector::CELL_ID_LOW24:
650       return "CELL_ID_LOW24_PASS";
651     case vtkHardwareSelector::CELL_ID_HIGH24:
652       return "CELL_ID_HIGH24_PASS";
653     default:
654       return "Invalid Enum";
655   }
656 }
657 
658 //------------------------------------------------------------------------------
IsPropHit(int id)659 bool vtkHardwareSelector::IsPropHit(int id)
660 {
661   return (this->Internals->HitProps.empty() ||
662     this->Internals->HitProps.find(id) != this->Internals->HitProps.end());
663 }
664 
665 //------------------------------------------------------------------------------
GetPixelInformation(const unsigned int in_display_position[2],int maxDistance,unsigned int out_selected_position[2])666 vtkHardwareSelector::PixelInformation vtkHardwareSelector::GetPixelInformation(
667   const unsigned int in_display_position[2], int maxDistance, unsigned int out_selected_position[2])
668 {
669   assert(in_display_position != out_selected_position);
670 
671   // Base case
672   unsigned int maxDist = (maxDistance < 0) ? 0 : static_cast<unsigned int>(maxDistance);
673   if (maxDist == 0)
674   {
675     out_selected_position[0] = in_display_position[0];
676     out_selected_position[1] = in_display_position[1];
677     if (in_display_position[0] < this->Area[0] || in_display_position[0] > this->Area[2] ||
678       in_display_position[1] < this->Area[1] || in_display_position[1] > this->Area[3])
679     {
680       return PixelInformation();
681     }
682 
683     // offset in_display_position based on the lower-left-corner of the Area.
684     unsigned int display_position[2] = { in_display_position[0] - this->Area[0],
685       in_display_position[1] - this->Area[1] };
686 
687     int actorid = this->Convert(display_position, this->PixBuffer[ACTOR_PASS]);
688     if (actorid <= 0)
689     {
690       // the pixel did not hit any actor.
691       return PixelInformation();
692     }
693 
694     PixelInformation info;
695     info.Valid = true;
696 
697     actorid -= ID_OFFSET;
698     info.PropID = actorid;
699     info.Prop = this->GetPropFromID(actorid);
700     if (this->ActorPassOnly)
701     {
702       return info;
703     }
704 
705     int composite_id = this->Convert(display_position, this->PixBuffer[COMPOSITE_INDEX_PASS]);
706     if (composite_id < 0 || composite_id > 0xffffff)
707     {
708       // the pixel did not hit any composite
709       return PixelInformation();
710     }
711     info.CompositeID = static_cast<unsigned int>(composite_id);
712 
713     int low24 = this->Convert(display_position, this->PixBuffer[CELL_ID_LOW24]);
714     int high24 = this->Convert(display_position, this->PixBuffer[CELL_ID_HIGH24]);
715     if (this->GetFieldAssociation() == vtkDataObject::FIELD_ASSOCIATION_POINTS)
716     {
717       low24 = this->Convert(display_position, this->PixBuffer[POINT_ID_LOW24]);
718       high24 = this->Convert(display_position, this->PixBuffer[POINT_ID_HIGH24]);
719     }
720 
721     info.AttributeID = this->GetID(low24, high24, 0);
722 
723     info.ProcessID =
724       this->Convert(display_position[0], display_position[1], this->PixBuffer[PROCESS_PASS]);
725     info.ProcessID -= ID_OFFSET;
726     return info;
727   }
728 
729   // Iterate over successively growing boxes.
730   // They recursively call the base case to handle single pixels.
731   unsigned int disp_pos[2] = { in_display_position[0], in_display_position[1] };
732   unsigned int cur_pos[2] = { 0, 0 };
733   PixelInformation info;
734   info = this->GetPixelInformation(in_display_position, 0, out_selected_position);
735   if (info.Valid)
736   {
737     return info;
738   }
739   for (unsigned int dist = 1; dist < maxDist; ++dist)
740   {
741     // Vertical sides of box.
742     for (unsigned int y = ((disp_pos[1] > dist) ? (disp_pos[1] - dist) : 0);
743          y <= disp_pos[1] + dist; ++y)
744     {
745       cur_pos[1] = y;
746       if (disp_pos[0] >= dist)
747       {
748         cur_pos[0] = disp_pos[0] - dist;
749         info = this->GetPixelInformation(cur_pos, 0, out_selected_position);
750         if (info.Valid)
751         {
752           return info;
753         }
754       }
755       cur_pos[0] = disp_pos[0] + dist;
756       info = this->GetPixelInformation(cur_pos, 0, out_selected_position);
757       if (info.Valid)
758       {
759         return info;
760       }
761     }
762     // Horizontal sides of box.
763     for (unsigned int x = ((disp_pos[0] >= dist) ? (disp_pos[0] - (dist - 1)) : 0);
764          x <= disp_pos[0] + (dist - 1); ++x)
765     {
766       cur_pos[0] = x;
767       if (disp_pos[1] >= dist)
768       {
769         cur_pos[1] = disp_pos[1] - dist;
770         info = this->GetPixelInformation(cur_pos, 0, out_selected_position);
771         if (info.Valid)
772         {
773           return info;
774         }
775       }
776       cur_pos[1] = disp_pos[1] + dist;
777       info = this->GetPixelInformation(cur_pos, 0, out_selected_position);
778       if (info.Valid)
779       {
780         return info;
781       }
782     }
783   }
784 
785   // nothing hit.
786   out_selected_position[0] = in_display_position[0];
787   out_selected_position[1] = in_display_position[1];
788   return PixelInformation();
789 }
790 
791 //------------------------------------------------------------------------------
GenerateSelection(unsigned int x1,unsigned int y1,unsigned int x2,unsigned int y2)792 vtkSelection* vtkHardwareSelector::GenerateSelection(
793   unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2)
794 {
795   vtkInternals::MapOfAttributeIds dataMap;
796   vtkInternals::PixelCountType pixelCounts;
797 
798   for (unsigned int yy = y1; yy <= y2; yy++)
799   {
800     for (unsigned int xx = x1; xx <= x2; xx++)
801     {
802       unsigned int pos[2] = { xx, yy };
803       PixelInformation info = this->GetPixelInformation(pos, 0);
804       if (info.Valid)
805       {
806         dataMap[info].insert(info.AttributeID);
807         pixelCounts[info]++;
808       }
809     }
810   }
811   return this->Internals->ConvertSelection(this->FieldAssociation, dataMap, pixelCounts);
812 }
813 
814 //------------------------------------------------------------------------------
GeneratePolygonSelection(int * polygonPoints,vtkIdType count)815 vtkSelection* vtkHardwareSelector::GeneratePolygonSelection(int* polygonPoints, vtkIdType count)
816 {
817   // we need at least three points (x,y) for a polygon selection.
818   if (!polygonPoints || count < 6)
819   {
820     return nullptr;
821   }
822 
823   int x1 = VTK_INT_MAX, x2 = VTK_INT_MIN, y1 = VTK_INT_MAX, y2 = VTK_INT_MIN;
824   // Get polygon bounds, so that we only check pixels within the bounds
825   for (vtkIdType i = 0; i < count; i += 2)
826   {
827     x1 = std::min(polygonPoints[i], x1);
828     x2 = std::max(polygonPoints[i], x2);
829     y1 = std::min(polygonPoints[i + 1], y1);
830     y2 = std::max(polygonPoints[i + 1], y2);
831   }
832 
833   vtkInternals::MapOfAttributeIds dataMap;
834   vtkInternals::PixelCountType pixelCounts;
835   for (int yy = y1; yy <= y2; yy++)
836   {
837     for (int xx = x1; xx <= x2; xx++)
838     {
839       if (this->Internals->PixelInsidePolygon(xx, yy, polygonPoints, count))
840       {
841         unsigned int pos[2] = { static_cast<unsigned int>(xx), static_cast<unsigned int>(yy) };
842         PixelInformation info = this->GetPixelInformation(pos, 0);
843         if (info.Valid)
844         {
845           dataMap[info].insert(info.AttributeID);
846           pixelCounts[info]++;
847         }
848       }
849     }
850   }
851   return this->Internals->ConvertSelection(this->FieldAssociation, dataMap, pixelCounts);
852 }
853 
854 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)855 void vtkHardwareSelector::PrintSelf(ostream& os, vtkIndent indent)
856 {
857   this->Superclass::PrintSelf(os, indent);
858   os << indent << "FieldAssociation: ";
859   switch (this->FieldAssociation)
860   {
861     case vtkDataObject::FIELD_ASSOCIATION_POINTS:
862       os << "FIELD_ASSOCIATION_POINTS" << endl;
863       break;
864     case vtkDataObject::FIELD_ASSOCIATION_CELLS:
865       os << "FIELD_ASSOCIATION_CELLS" << endl;
866       break;
867     case vtkDataObject::FIELD_ASSOCIATION_VERTICES:
868       os << "FIELD_ASSOCIATION_VERTICES" << endl;
869       break;
870     case vtkDataObject::FIELD_ASSOCIATION_EDGES:
871       os << "FIELD_ASSOCIATION_EDGES" << endl;
872       break;
873     case vtkDataObject::FIELD_ASSOCIATION_ROWS:
874       os << "FIELD_ASSOCIATION_ROWS" << endl;
875       break;
876     default:
877       os << "--unknown--" << endl;
878   }
879   os << indent << "ProcessID: " << this->ProcessID << endl;
880   os << indent << "CurrentPass: " << this->CurrentPass << endl;
881   os << indent << "Area: " << this->Area[0] << ", " << this->Area[1] << ", " << this->Area[2]
882      << ", " << this->Area[3] << endl;
883   os << indent << "Renderer: " << this->Renderer << endl;
884   os << indent << "UseProcessIdFromData: " << this->UseProcessIdFromData << endl;
885   os << indent << "ActorPassOnly: " << this->ActorPassOnly << endl;
886 }
887