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