VTK  9.7.1
vtkBlockSortHelper.h
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
2 // SPDX-License-Identifier: BSD-3-Clause
8 #ifndef vtkBlockSortHelper_h
9 #define vtkBlockSortHelper_h
10 
11 #include "vtkCamera.h"
12 #include "vtkDataSet.h"
13 #include "vtkMatrix4x4.h"
14 #include "vtkNew.h"
15 #include "vtkRenderer.h"
16 #include "vtkVector.h"
17 
18 #include <vector>
19 
21 {
22 VTK_ABI_NAMESPACE_BEGIN
23 
24 template <typename T>
25 inline void GetBounds(T a, double bds[6])
26 {
27  a.GetBounds(bds);
28 }
29 
30 template <>
31 inline void GetBounds(vtkDataSet* first, double bds[6])
32 {
33  first->GetBounds(bds);
34 }
35 
42 template <typename T>
44 {
48 
49  //----------------------------------------------------------------------------
51  {
52  vtkCamera* cam = ren->GetActiveCamera();
53  this->CameraIsParallel = (cam->GetParallelProjection() != 0);
54 
55  double camWorldPos[4];
56  cam->GetPosition(camWorldPos);
57  camWorldPos[3] = 1.0;
58 
59  double camWorldFocalPoint[4];
60  cam->GetFocalPoint(camWorldFocalPoint);
61  camWorldFocalPoint[3] = 1.0;
62 
63  // Transform the camera position to the volume (dataset) coordinate system.
64  vtkNew<vtkMatrix4x4> InverseVolumeMatrix;
65  InverseVolumeMatrix->DeepCopy(volMatrix);
66  InverseVolumeMatrix->Invert();
67  InverseVolumeMatrix->MultiplyPoint(camWorldPos, camWorldPos);
68  InverseVolumeMatrix->MultiplyPoint(camWorldFocalPoint, camWorldFocalPoint);
69 
70  this->CameraPosition = vtkVector3d(camWorldPos[0], camWorldPos[1], camWorldPos[2]);
71  this->CameraPosition = this->CameraPosition / vtkVector3d(camWorldPos[3]);
72 
73  vtkVector3d camFP(camWorldFocalPoint[0], camWorldFocalPoint[1], camWorldFocalPoint[2]);
74  camFP = camFP / vtkVector3d(camWorldFocalPoint[3]);
75 
76  this->CameraViewDirection = camFP - this->CameraPosition;
77  }
78 
79  //----------------------------------------------------------------------------
80  // camPos is used when is_parallel is false, else viewdirection is used.
81  // thus a valid camPos is only needed if is_parallel is false, and a valid viewdirection
82  // is only needed if is_parallel is true.
83  BackToFront(const vtkVector3d& camPos, const vtkVector3d& viewdirection, bool is_parallel)
84  : CameraPosition(camPos)
85  , CameraViewDirection(viewdirection)
86  , CameraIsParallel(is_parallel)
87  {
88  }
89 
90  // -1 if first is closer than second
91  // 0 if unknown
92  // 1 if second is farther than first
93  template <typename TT>
94  int CompareOrderWithUncertainty(TT& first, TT& second)
95  {
96  double abounds[6], bbounds[6];
97  vtkBlockSortHelper::GetBounds<TT>(first, abounds);
98  vtkBlockSortHelper::GetBounds<TT>(second, bbounds);
99  return CompareBoundsOrderWithUncertainty(abounds, bbounds);
100  }
101 
102  // -1 if first is closer than second
103  // 0 if unknown
104  // 1 if second is farther than first
105  int CompareBoundsOrderWithUncertainty(const double abounds[6], const double bbounds[6])
106  {
107  double bboundsP[6];
108  double aboundsP[6];
109  for (int i = 0; i < 6; ++i)
110  {
111  int low = 2 * (i / 2);
112  bboundsP[i] = bbounds[i];
113  bboundsP[i] = std::max(bboundsP[i], abounds[low]);
114  bboundsP[i] = std::min(bboundsP[i], abounds[low + 1]);
115  aboundsP[i] = abounds[i];
116  aboundsP[i] = std::max(aboundsP[i], bbounds[low]);
117  aboundsP[i] = std::min(aboundsP[i], bbounds[low + 1]);
118  }
119 
120  int dims = 0;
121  int degenDims = 0;
122  int degenAxes[3];
123  int dimSize[3];
124  for (int i = 0; i < 6; i += 2)
125  {
126  if (aboundsP[i] != aboundsP[i + 1])
127  {
128  dimSize[dims] = aboundsP[i + 1] - aboundsP[i];
129  dims++;
130  }
131  else
132  {
133  degenAxes[degenDims] = i / 2;
134  degenDims++;
135  }
136  }
137 
138  // overlapping volumes?
139  // in that case find the two largest dimensions
140  // geneally this should not happen
141  if (dims == 3)
142  {
143  if (dimSize[0] < dimSize[1])
144  {
145  if (dimSize[0] < dimSize[2])
146  {
147  degenAxes[0] = 0;
148  }
149  else
150  {
151  degenAxes[0] = 2;
152  }
153  }
154  else
155  {
156  if (dimSize[1] < dimSize[2])
157  {
158  degenAxes[0] = 1;
159  }
160  else
161  {
162  degenAxes[0] = 2;
163  }
164  }
165  dims = 2;
166  degenDims = 1;
167  }
168 
169  // compute the direction from a to b
170  double atobdir[3] = { bbounds[0] + bbounds[1] - abounds[0] - abounds[1],
171  bbounds[2] + bbounds[3] - abounds[2] - abounds[3],
172  bbounds[4] + bbounds[5] - abounds[4] - abounds[5] };
173  double atoblength = vtkMath::Normalize(atobdir);
174 
175  // no comment on blocks that do not touch
176  if (fabs(aboundsP[degenAxes[0] * 2] - bboundsP[degenAxes[0] * 2]) > 0.01 * atoblength)
177  {
178  return 0;
179  }
180 
181  // compute the camera direction
183  if (this->CameraIsParallel)
184  {
185  dir = this->CameraViewDirection;
186  }
187  else
188  {
189  // compute a point for the half space plane
190  vtkVector3d planePoint(0.25 * (aboundsP[0] + aboundsP[1] + bboundsP[0] + bboundsP[1]),
191  0.25 * (aboundsP[2] + aboundsP[3] + bboundsP[2] + bboundsP[3]),
192  0.25 * (aboundsP[4] + aboundsP[5] + bboundsP[4] + bboundsP[5]));
193  dir = planePoint - this->CameraPosition;
194  }
195  dir.Normalize();
196 
197  // planar interface
198  if (dims == 2)
199  {
200  double plane[3] = { 0, 0, 0 };
201  plane[degenAxes[0]] = 1.0;
202  // dot product with camera direction
203  double dot = dir[0] * plane[0] + dir[1] * plane[1] + dir[2] * plane[2];
204  if (dot == 0)
205  {
206  return 0;
207  }
208  // figure out what side we are on
209  double side = plane[0] * atobdir[0] + plane[1] * atobdir[1] + plane[2] * atobdir[2];
210  return (dot * side < 0 ? 1 : -1);
211  }
212 
213  return 0;
214  }
215 };
216 
217 #ifdef MB_DEBUG
218 template <class RandomIt>
219 class gnode
220 {
221 public:
222  RandomIt Value;
223  bool Visited = false;
224  std::vector<gnode<RandomIt>*> Closer;
225 };
226 
227 template <class RandomIt>
228 bool operator==(gnode<RandomIt> const& lhs, gnode<RandomIt> const& rhs)
229 {
230  return lhs.Value == rhs.Value && lhs.Closer == rhs.Closer;
231 }
232 
233 template <class RandomIt>
234 bool findCycle(gnode<RandomIt>& start, std::vector<gnode<RandomIt>>& graph,
235  std::vector<gnode<RandomIt>>& active, std::vector<gnode<RandomIt>>& loop)
236 {
237  if (start.Visited)
238  {
239  return false;
240  }
241 
242  // add the current node to active list
243  active.push_back(start);
244 
245  // traverse the closer nodes one by one depth first
246  for (auto& close : start.Closer)
247  {
248  if (close->Visited)
249  {
250  continue;
251  }
252 
253  // is the node already in the active list? if so we have a loop
254  for (auto ait = active.begin(); ait != active.end(); ++ait)
255  {
256  if (ait->Value == close->Value)
257  {
258  loop.push_back(*ait);
259  return true;
260  }
261  }
262  // otherwise recurse
263  if (findCycle(*close, graph, active, loop))
264  {
265  // a loop was detected, build the loop output
266  loop.push_back(*close);
267  return true;
268  }
269  }
270 
271  active.erase(std::find(active.begin(), active.end(), start));
272  start.Visited = true;
273  return false;
274 }
275 #endif
276 
277 template <class RandomIt, typename T>
278 inline void Sort(RandomIt bitr, RandomIt eitr, BackToFront<T>& me)
279 {
280  auto start = bitr;
281 
282  // brute force for testing
283 
284  std::vector<typename RandomIt::value_type> working;
285  std::vector<typename RandomIt::value_type> result;
286  working.assign(bitr, eitr);
287  size_t numNodes = working.size();
288 
289 #ifdef MB_DEBUG
290  // check for any short loops and debug
291  for (auto it = working.begin(); it != working.end(); ++it)
292  {
293  auto it2 = it;
294  it2++;
295  for (; it2 != working.end(); ++it2)
296  {
297  int comp1 = me.CompareOrderWithUncertainty(*it, *it2);
298  int comp2 = me.CompareOrderWithUncertainty(*it2, *it);
299  if (comp1 * comp2 > 0)
300  {
301  me.CompareOrderWithUncertainty(*it, *it2);
302  me.CompareOrderWithUncertainty(*it2, *it);
303  }
304  }
305  }
306 
307  // build the graph
308  std::vector<gnode<RandomIt>> graph;
309  for (auto it = working.begin(); it != working.end(); ++it)
310  {
311  gnode<RandomIt> anode;
312  anode.Value = it;
313  graph.push_back(anode);
314  }
315  for (auto& git : graph)
316  {
317  for (auto& next : graph)
318  {
319  if (git.Value != next.Value && me.CompareOrderWithUncertainty(*git.Value, *next.Value) > 0)
320  {
321  git.Closer.push_back(&next);
322  }
323  }
324  }
325 
326  // graph constructed, now look for a loop
327  std::vector<gnode<RandomIt>> active;
328  std::vector<gnode<RandomIt>> loop;
329  for (auto& gval : graph)
330  {
331  loop.clear();
332  if (findCycle(gval, graph, active, loop))
333  {
335  dir.Normalize();
336  vtkGenericWarningMacro("found a loop cam dir: " << dir[0] << " " << dir[1] << " " << dir[2]);
337  for (auto& lval : loop)
338  {
339  double bnds[6];
340  vtkBlockSortHelper::GetBounds((*lval.Value), bnds);
341  vtkGenericWarningMacro(<< bnds[0] << " " << bnds[1] << " " << bnds[2] << " " << bnds[3]
342  << " " << bnds[4] << " " << bnds[5]);
343  }
344  }
345  }
346 #endif
347 
348  // loop over items and find the first that is not in front of any others
349  for (auto it = working.begin(); it != working.end();)
350  {
351  auto it2 = working.begin();
352  for (; it2 != working.end(); ++it2)
353  {
354  // if another block is in front of this block then this is not the
355  // closest block
356  if (it != it2 && me.CompareOrderWithUncertainty(*it, *it2) > 0)
357  {
358  // not a winner
359  break;
360  }
361  }
362  if (it2 == working.end())
363  {
364  // found a winner, add it to the results, remove from the working set and then restart
365  result.push_back(*it);
366  working.erase(it);
367  it = working.begin();
368  }
369  else
370  {
371  ++it;
372  }
373  }
374 
375  if (result.size() != numNodes)
376  {
377  vtkGenericWarningMacro("sorting failed");
378  }
379 
380  // copy results to original container
381  std::reverse_copy(result.begin(), result.end(), start);
382 };
383 VTK_ABI_NAMESPACE_END
384 }
385 
386 #endif // vtkBlockSortHelper_h
387 // VTK-HeaderTest-Exclude: vtkBlockSortHelper.h
void GetBounds(T a, double bds[6])
double Normalize()
Normalize the vector in place.
Definition: vtkVector.h:77
represent and manipulate 4x4 transformation matrices
Definition: vtkMatrix4x4.h:31
double * GetBounds()
Return a pointer to the geometry bounding box in the form (xmin,xmax, ymin,ymax, zmin,zmax).
abstract class to specify dataset behavior
Definition: vtkDataSet.h:56
int CompareOrderWithUncertainty(TT &first, TT &second)
void Sort(RandomIt bitr, RandomIt eitr, BackToFront< T > &me)
abstract specification for renderers
Definition: vtkRenderer.h:63
bool VTKCOMMONCORE_EXPORT operator==(const std::string &a, const vtkStringToken &b)
vtkCamera * GetActiveCamera()
Get the current camera.
BackToFront(const vtkVector3d &camPos, const vtkVector3d &viewdirection, bool is_parallel)
virtual double * GetPosition()
Set/Get the position of the camera in world coordinates.
void MultiplyPoint(const float in[4], float out[4])
Multiply a homogeneous coordinate by this matrix, i.e.
Definition: vtkMatrix4x4.h:148
void DeepCopy(const vtkMatrix4x4 *source)
Set the elements of the matrix to the same values as the elements of the given source matrix...
Definition: vtkMatrix4x4.h:49
int CompareBoundsOrderWithUncertainty(const double abounds[6], const double bbounds[6])
virtual double * GetFocalPoint()
Set/Get the focal of the camera in world coordinates.
BackToFront(vtkRenderer *ren, vtkMatrix4x4 *volMatrix)
static void Invert(const vtkMatrix4x4 *in, vtkMatrix4x4 *out)
Matrix Inversion (adapted from Richard Carling in "Graphics Gems," Academic Press, 1990).
Definition: vtkMatrix4x4.h:109
a virtual camera for 3D rendering
Definition: vtkCamera.h:41
operator() for back-to-front sorting.
static float Normalize(float v[3])
Normalize (in place) a 3-vector.
Definition: vtkMath.h:1863
Collection of comparison functions for std::sort.
virtual vtkTypeBool GetParallelProjection()
Set/Get the value of the ParallelProjection instance variable.
#define max(a, b)