ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
cvHardwareSelector.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
18 #include "cvHardwareSelector.h"
19 
20 // CloudViewer
21 #include <CVLog.h>
22 #include <vtkActor.h>
23 #include <vtkActorCollection.h>
24 #include <vtkDataObject.h>
25 #include <vtkMapper.h>
26 #include <vtkObjectFactory.h>
27 #include <vtkProp.h>
28 #include <vtkRenderWindow.h>
29 #include <vtkRenderer.h>
30 #include <vtkSelection.h>
31 
32 // For debug output (optional)
33 // #define cvHardwareSelectorDEBUG
34 #ifdef cvHardwareSelectorDEBUG
35 #include <vtkImageImport.h>
36 #include <vtkNew.h>
37 #include <vtkPNMWriter.h>
38 
39 #include <sstream>
40 #endif
41 
42 //----------------------------------------------------------------------------
44 
45 //----------------------------------------------------------------------------
47  // ParaView-style: Use process ID from data
48  // Reference: vtkPVHardwareSelector constructor line 43
49  this->SetUseProcessIdFromData(true);
50  this->ProcessID = 0;
51  this->UniqueId = 0;
52  // ParaView code default is 0 (disabled), but settings XML sets it to 50
53  this->PointPickingRadius = 50;
54 }
55 
56 //----------------------------------------------------------------------------
57 cvHardwareSelector::~cvHardwareSelector() { this->PropMap.clear(); }
58 
59 //----------------------------------------------------------------------------
61  // Reference: vtkPVHardwareSelector::PassRequired() lines 63-84
62  // Always require the process pass on first iteration
63  if (pass == PROCESS_PASS && this->Iteration == 0) {
64  return true;
65  }
66 
67  return this->Superclass::PassRequired(pass);
68 }
69 
70 //----------------------------------------------------------------------------
72  // Reference: vtkPVHardwareSelector::PrepareSelect() lines 87-102
73  bool needRender = this->NeedToRenderForSelection();
74 
75  if (needRender) {
76  if (!this->Renderer) {
78  "[cvHardwareSelector::PrepareSelect] No renderer set!");
79  return false;
80  }
81 
82  int* size = this->Renderer->GetSize();
83  int* origin = this->Renderer->GetOrigin();
84 
85  this->SetArea(origin[0], origin[1], origin[0] + size[0] - 1,
86  origin[1] + size[1] - 1);
87 
88  if (this->CaptureBuffers() == false) {
90  "[cvHardwareSelector::PrepareSelect] CaptureBuffers "
91  "failed!");
92  this->CaptureTime.Modified();
93  return false;
94  }
95  this->CaptureTime.Modified();
96  }
97  return true;
98 }
99 
100 //----------------------------------------------------------------------------
101 vtkSelection* cvHardwareSelector::Select(int region[4]) {
102  // Reference: vtkPVHardwareSelector::Select() lines 105-131
103  if (!this->PrepareSelect()) {
104  CVLog::Error("[cvHardwareSelector::Select] PrepareSelect failed!");
105  return nullptr;
106  }
107 
108  vtkSelection* sel =
109  this->GenerateSelection(region[0], region[1], region[2], region[3]);
110 
111  // ParaView-style Point Picking Radius support
112  // Reference: vtkPVHardwareSelector::Select() lines 113-129
113  // Only applies to:
114  // 1. Empty selection (no direct hit)
115  // 2. Point selection (not cell)
116  // 3. Single point click (not drag)
117  // 4. PointPickingRadius > 0
118  bool noNodes = (sel->GetNumberOfNodes() == 0);
119  bool isPointSelection =
120  (this->FieldAssociation == vtkDataObject::FIELD_ASSOCIATION_POINTS);
121  bool isSingleClick = (region[0] == region[2] && region[1] == region[3]);
122  bool hasRadius = (this->PointPickingRadius > 0);
123 
124  if (noNodes && isPointSelection && isSingleClick && hasRadius) {
125  unsigned int pos[2];
126  pos[0] = static_cast<unsigned int>(region[0]);
127  pos[1] = static_cast<unsigned int>(region[1]);
128 
129  unsigned int out_pos[2];
130  vtkHardwareSelector::PixelInformation info = this->GetPixelInformation(
131  pos, this->PointPickingRadius, out_pos);
132 
133  if (info.Valid) {
134  sel->Delete();
135  return this->GenerateSelection(out_pos[0], out_pos[1], out_pos[0],
136  out_pos[1]);
137  }
138  }
139  return sel;
140 }
141 
142 //----------------------------------------------------------------------------
143 vtkSelection* cvHardwareSelector::PolygonSelect(int* polygonPoints,
144  vtkIdType count) {
145  // Reference: vtkPVHardwareSelector::PolygonSelect() lines 134-141
146  if (!this->PrepareSelect()) {
147  return nullptr;
148  }
149  return this->GeneratePolygonSelection(polygonPoints, count);
150 }
151 
152 //----------------------------------------------------------------------------
154  // Reference: vtkPVHardwareSelector::NeedToRenderForSelection() lines
155  // 144-150 We rely on external logic to ensure that the MTime for the
156  // cvHardwareSelector is explicitly modified when some action happens that
157  // would result in invalidation of captured buffers.
158  return this->CaptureTime < this->GetMTime();
159 }
160 
161 //----------------------------------------------------------------------------
163  // Reference: vtkPVHardwareSelector::AssignUniqueId() lines 153-159
164  int id = this->UniqueId;
165  this->UniqueId++;
166  this->PropMap[prop] = id;
167  return id;
168 }
169 
170 //----------------------------------------------------------------------------
171 int cvHardwareSelector::GetPropID(int idx, vtkProp* prop) {
172  // Reference: vtkPVHardwareSelector::GetPropID() lines 162-166
173  // First try to find in PropMap (for explicitly assigned IDs via
174  // AssignUniqueId)
175  auto iter = this->PropMap.find(prop);
176  if (iter != this->PropMap.end()) {
177  return iter->second;
178  }
179 
180  // CRITICAL FIX: Fall back to VTK's default behavior using idx
181  // ParaView's vtkPVRenderView calls AssignUniqueId to populate PropMap,
182  // but ACloudViewer doesn't have this logic. Without this fallback,
183  // GetPropID returns -1, causing ACTOR_PASS to output 0 (invalid ID).
184  // The idx parameter is the sequential index of the prop being rendered,
185  // which VTK uses as the default prop ID.
186  return idx;
187 }
188 
189 //----------------------------------------------------------------------------
190 void cvHardwareSelector::PrintSelf(ostream& os, vtkIndent indent) {
191  this->Superclass::PrintSelf(os, indent);
192  os << indent << "PointPickingRadius: " << this->PointPickingRadius << "\n";
193  os << indent << "UniqueId: " << this->UniqueId << "\n";
194  os << indent << "PropMap size: " << this->PropMap.size() << "\n";
195 }
196 
197 //----------------------------------------------------------------------------
199  // Call parent which sets this->Renderer->SetSelector(this)
200  this->Superclass::BeginSelection();
201 }
202 
203 //----------------------------------------------------------------------------
204 void cvHardwareSelector::EndSelection() { this->Superclass::EndSelection(); }
205 
206 //----------------------------------------------------------------------------
207 void cvHardwareSelector::BeginRenderProp(vtkRenderWindow* rw) {
208  // Reference: vtkPVHardwareSelector::BeginRenderProp() lines 175-181
209  // In ParaView, this sets ProcessID from vtkProcessModule
210  // For ACloudViewer (single process), we keep ProcessID = 0
211  this->Superclass::BeginRenderProp(rw);
212 }
213 
214 //----------------------------------------------------------------------------
216  // Reference: vtkPVHardwareSelector::SavePixelBuffer() lines 185-206
217  this->Superclass::SavePixelBuffer(passNo);
218 
219 #ifdef cvHardwareSelectorDEBUG
220  vtkNew<vtkImageImport> ii;
221  ii->SetImportVoidPointer(this->PixBuffer[passNo], 1);
222  ii->SetDataScalarTypeToUnsignedChar();
223  ii->SetNumberOfScalarComponents(3);
224  ii->SetDataExtent(this->Area[0], this->Area[2], this->Area[1],
225  this->Area[3], 0, 0);
226  ii->SetWholeExtent(this->Area[0], this->Area[2], this->Area[1],
227  this->Area[3], 0, 0);
228 
229  std::ostringstream fname;
230  fname << "/tmp/cv-buffer-pass-" << passNo << ".pnm";
231  vtkNew<vtkPNMWriter> pw;
232  pw->SetInputConnection(ii->GetOutputPort());
233  pw->SetFileName(fname.str().c_str());
234  pw->Write();
235 #endif
236 }
int size
int count
Eigen::Vector3d origin
Definition: VoxelGridIO.cpp:26
static bool Error(const char *format,...)
Display an error dialog with formatted message.
Definition: CVLog.cpp:143
Hardware selector with ParaView-style buffer caching.
int AssignUniqueId(vtkProp *prop)
Assign a unique ID to a prop.
int GetPropID(int idx, vtkProp *prop) override
Return a unique ID for the prop.
int UniqueId
Counter for unique prop IDs.
void EndSelection() override
End selection - clears selector from renderer.
void PrintSelf(ostream &os, vtkIndent indent) override
void BeginRenderProp(vtkRenderWindow *rw) override
Begin render prop (sets ProcessID)
virtual bool NeedToRenderForSelection()
Check if next Select() will need to render.
virtual vtkSelection * Select(int region[4])
Perform selection over the specified region.
vtkTimeStamp CaptureTime
Time when buffers were last captured.
bool PassRequired(int pass) override
Check if a pass is required.
void SavePixelBuffer(int passNo) override
Save pixel buffer (with optional debug output)
unsigned int PointPickingRadius
Point picking radius in pixels (default: 10)
vtkSelection * PolygonSelect(int *polygonPoints, vtkIdType count)
Perform polygon selection.
void BeginSelection() override
Begin selection - sets selector on renderer.
bool PrepareSelect()
Prepare for selection.
vtkStandardNewMacro(cvHardwareSelector)