Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
REvePolygonSetProjected.cxx
Go to the documentation of this file.
1 // @(#)root/eve7:$Id$
2 // Authors: Matevz Tadel & Alja Mrak-Tadel: 2006, 2007
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
13 #include <ROOT/REveGeoShape.hxx>
15 #include <ROOT/REveGluTess.hxx>
16 #include <ROOT/REveRenderData.hxx>
17 
18 #include "TBuffer3D.h"
19 #include "TBuffer3DTypes.h"
20 
21 #include "json.hpp"
22 #include <cassert>
23 
24 using namespace ROOT::Experimental;
25 namespace REX = ROOT::Experimental;
26 
27 namespace
28 {
29  struct Seg_t
30  {
31  // Helper class for building 2D polygons from TBuffer3D.
32  Int_t fV1;
33  Int_t fV2;
34 
35  Seg_t(Int_t i1=-1, Int_t i2=-1) : fV1(i1), fV2(i2) {}
36  };
37 
38  typedef std::list<Seg_t> LSeg_t;
39 }
40 
41 /** \class REvePolygonSetProjected
42 \ingroup REve
43 A set of projected polygons.
44 Used for storage of projected geometrical shapes.
45 
46 Internal struct Polygon_t holds only indices into the master vertex
47 array in REvePolygonSetProjected.
48 */
49 
50 ////////////////////////////////////////////////////////////////////////////////
51 /// Constructor.
52 
53 REvePolygonSetProjected::REvePolygonSetProjected(const std::string &n, const std::string &t) :
54  REveShape(n, t),
55  fBuff(),
56  fPnts()
57 {
58 }
59 
60 ////////////////////////////////////////////////////////////////////////////////
61 /// Destructor.
62 
63 REvePolygonSetProjected::~REvePolygonSetProjected()
64 {
65  fPols.clear();
66 }
67 
68 ////////////////////////////////////////////////////////////////////////////////
69 /// Fill core part of JSON representation.
70 
71 Int_t REvePolygonSetProjected::WriteCoreJson(nlohmann::json& j, Int_t rnr_offset)
72 {
73  Int_t ret = REveElement::WriteCoreJson(j, rnr_offset);
74 
75  j["fNPnts"] = fPnts.size();
76 
77  return ret;
78 }
79 
80 ////////////////////////////////////////////////////////////////////////////////
81 /// Crates representation for rendering.
82 /// This is complicated as we need to:
83 /// - generate outlines;
84 /// - convert polygons to triangles.
85 /// ??? Should we check if polygons are front facing? It was not done in old EVE,
86 /// just GL_FRONT_AND_BACK was used on top of gluTess.
87 
88 void REvePolygonSetProjected::BuildRenderData()
89 {
90  fRenderData = std::make_unique<REveRenderData>("makePolygonSetProjected", 3 * fPnts.size());
91 
92  Int_t n_pols = fPols.size();
93  Int_t n_poly_info = 0;
94  for (auto &p : fPols) n_poly_info += 1 + p.NPoints();
95 
96  std::vector<Double_t> verts;
97  verts.reserve(3 * fPnts.size());
98  std::vector<Int_t> polys;
99  polys.reserve(n_poly_info);
100 
101  for (auto &p : fPols)
102  {
103  polys.emplace_back(p.NPoints());
104  polys.insert(polys.end(), p.fPnts.begin(), p.fPnts.end());
105  }
106 
107  for (unsigned i = 0; i < fPnts.size(); ++i)
108  {
109  verts.push_back(fPnts[i].fX);
110  verts.push_back(fPnts[i].fY);
111  verts.push_back(fPnts[i].fZ);
112  fRenderData->PushV(fPnts[i]);
113  }
114 
115  Int_t n_trings = 0;
116  {
117  EveGlu::TriangleCollector tc;
118 
119  tc.ProcessData(verts, polys, n_pols);
120 
121  polys.swap(tc.RefPolyDesc());
122  n_trings = tc.GetNTrianlges();
123  }
124 
125  // Calculate size of index buffer.
126  Int_t n_idxbuff = 2 + 3 * n_trings + n_pols + n_poly_info;
127  fRenderData->Reserve(0,0,n_idxbuff);
128 
129  assert(n_trings * 4 == (int)polys.size());
130 
131  // Export triangles.
132  fRenderData->PushI(REveRenderData::GL_TRIANGLES);
133  fRenderData->PushI(n_trings);
134  for (int i = 0; i < n_trings; ++i)
135  {
136  fRenderData->PushI(&polys[i*4 + 1], 3);
137  }
138 
139  assert (fRenderData->SizeI() == 2 + 3 * n_trings);
140 
141  // Export outlines.
142  for (auto &p : fPols)
143  {
144  fRenderData->PushI(REveRenderData::GL_LINE_LOOP);
145  fRenderData->PushI(p.NPoints());
146  fRenderData->PushI(p.fPnts);
147  }
148 
149  assert (fRenderData->SizeI() == n_idxbuff);
150 }
151 
152 ////////////////////////////////////////////////////////////////////////////////
153 /// Override of virtual method from TAttBBox.
154 
155 void REvePolygonSetProjected::ComputeBBox()
156 {
157  if (fPnts.size() > 0) {
158  BBoxInit();
159  for (unsigned pi = 0; pi < fPnts.size(); ++pi)
160  BBoxCheckPoint(fPnts[pi].fX, fPnts[pi].fY, fPnts[pi].fZ);
161  } else {
162  BBoxZero();
163  }
164 }
165 
166 ////////////////////////////////////////////////////////////////////////////////
167 /// This is virtual method from base-class REveProjected.
168 
169 void REvePolygonSetProjected::SetProjection(REveProjectionManager* mng,
170  REveProjectable* model)
171 {
172  REveProjected::SetProjection(mng, model);
173 
174  REveGeoShape* gre = dynamic_cast<REveGeoShape*>(model);
175  fBuff = gre->MakeBuffer3D();
176  CopyVizParams(gre);
177 }
178 
179 ////////////////////////////////////////////////////////////////////////////////
180 /// Set depth (z-coordinate) of the projected points.
181 
182 void REvePolygonSetProjected::SetDepthLocal(Float_t d)
183 {
184  SetDepthCommon(d, this, fBBox);
185 
186  for (unsigned i = 0; i < fPnts.size(); ++i)
187  fPnts[i].fZ = fDepth;
188 }
189 
190 ////////////////////////////////////////////////////////////////////////////////
191 /// This is virtual method from base-class REveProjected.
192 
193 void REvePolygonSetProjected::UpdateProjection()
194 {
195  if (!fBuff) return;
196 
197  // drop polygons and projected/reduced points
198  fPols.clear();
199  ProjectBuffer3D();
200 }
201 
202 ////////////////////////////////////////////////////////////////////////////////
203 /// Compare the two segments and check if the first index of first segment is starting.
204 
205 Bool_t REvePolygonSetProjected::IsFirstIdxHead(Int_t s0, Int_t s1)
206 {
207  Int_t v0 = fBuff->fSegs[3*s0 + 1];
208  Int_t v2 = fBuff->fSegs[3*s1 + 1];
209  Int_t v3 = fBuff->fSegs[3*s1 + 2];
210  return v0 != v2 && v0 != v3;
211 }
212 
213 ////////////////////////////////////////////////////////////////////////////////
214 /// Project and reduce buffer points.
215 
216 std::vector<Int_t> REvePolygonSetProjected::ProjectAndReducePoints()
217 {
218  REveProjection* projection = fManager->GetProjection();
219 
220  Int_t buffN = fBuff->NbPnts();
221  std::vector<REveVector> pnts; pnts.resize(buffN);
222  for (Int_t i = 0; i < buffN; ++i)
223  {
224  pnts[i].Set(fBuff->fPnts[3*i],fBuff->fPnts[3*i+1], fBuff->fPnts[3*i+2]);
225  projection->ProjectPoint(pnts[i].fX, pnts[i].fY, pnts[i].fZ, 0,
226  REveProjection::kPP_Plane);
227  }
228 
229  int npoints = 0;
230  std::vector<Int_t> idxMap;
231  idxMap.resize(buffN);
232 
233  std::vector<int> ra;
234  ra.resize(buffN); // list of reduced vertices
235  for (UInt_t v = 0; v < (UInt_t)buffN; ++v)
236  {
237  idxMap[v] = -1;
238  for (Int_t k = 0; k < npoints; ++k)
239  {
240  if (pnts[v].SquareDistance(pnts[ra[k]]) < REveProjection::fgEpsSqr)
241  {
242  idxMap[v] = k;
243  break;
244  }
245  }
246  // have not found a point inside epsilon, add new point in scaled array
247  if (idxMap[v] == -1)
248  {
249  idxMap[v] = npoints;
250  ra[npoints] = v;
251  ++npoints;
252  }
253  }
254 
255  // write the array of scaled points
256  fPnts.resize(npoints);
257  for (Int_t idx = 0; idx < npoints; ++idx)
258  {
259  Int_t i = ra[idx];
260  projection->ProjectPoint(pnts[i].fX, pnts[i].fY, pnts[i].fZ, fDepth,
261  REveProjection::kPP_Distort);
262  fPnts[idx].Set(pnts[i]);
263  }
264  // printf("reduced %d points of %d\n", fNPnts, N);
265 
266  return idxMap;
267 }
268 
269 ////////////////////////////////////////////////////////////////////////////////
270 /// Check if polygon has dimensions above REveProjection::fgEps and add it
271 /// to a list if it is not a duplicate.
272 
273 Float_t REvePolygonSetProjected::AddPolygon(std::list<Int_t> &pp, vpPolygon_t &pols)
274 {
275  if (pp.size() <= 2) return 0;
276 
277  Float_t bbox[4] = { 1e6, -1e6, 1e6, -1e6 };
278  for (auto &&idx: pp)
279  {
280  if (fPnts[idx].fX < bbox[0]) bbox[0] = fPnts[idx].fX;
281  if (fPnts[idx].fX > bbox[1]) bbox[1] = fPnts[idx].fX;
282 
283  if (fPnts[idx].fY < bbox[2]) bbox[2] = fPnts[idx].fY;
284  if (fPnts[idx].fY > bbox[3]) bbox[3] = fPnts[idx].fY;
285  }
286  Float_t eps = 2*REveProjection::fgEps;
287  if ((bbox[1]-bbox[0]) < eps || (bbox[3]-bbox[2]) < eps) return 0;
288 
289  // Duplication
290  for (auto &&refP : pols)
291  {
292  if ((Int_t) pp.size() != refP.NPoints())
293  continue;
294 
295  Int_t start_idx = refP.FindPoint(pp.front());
296  if (start_idx < 0)
297  continue;
298  if (++start_idx >= refP.NPoints()) start_idx = 0;
299 
300  // Same orientation duplicate
301  {
302  auto u = ++pp.begin();
303  Int_t pidx = start_idx;
304  while (u != pp.end())
305  {
306  if ((*u) != refP.fPnts[pidx])
307  break;
308  ++u;
309  if (++pidx >= refP.NPoints()) pidx = 0;
310  }
311  if (u == pp.end()) return 0;
312  }
313  // Inverse orientation duplicate
314  {
315  auto u = --pp.end();
316  Int_t pidx = start_idx;
317  while (u != pp.begin())
318  {
319  if ((*u) != refP.fPnts[pidx])
320  break;
321  --u;
322  if (++pidx >= refP.NPoints()) pidx = 0;
323  }
324  if (u == pp.begin()) return 0;
325  }
326  }
327 
328  std::vector<int> pv(pp.size(), 0);
329  int count = 0;
330  for (auto &&u : pp) {
331  pv[count++] = u;
332  }
333 
334  pols.emplace_back(std::move(pv));
335 
336  return (bbox[1]-bbox[0]) * (bbox[3]-bbox[2]);
337 }
338 
339 ////////////////////////////////////////////////////////////////////////////////
340 /// Build polygons from list of buffer polygons.
341 
342 Float_t REvePolygonSetProjected::MakePolygonsFromBP(std::vector<Int_t> &idxMap)
343 {
344  REveProjection* projection = fManager->GetProjection();
345  Int_t *bpols = fBuff->fPols;
346  Float_t surf = 0; // surface of projected polygons
347  for (UInt_t pi = 0; pi < fBuff->NbPols(); ++pi)
348  {
349  std::list<Int_t> pp; // points in current polygon
350  UInt_t segN = bpols[1];
351  Int_t *seg = &bpols[2];
352  // start idx in the fist segment depends of second segment
353  Int_t tail, head;
354  if (IsFirstIdxHead(seg[0], seg[1]))
355  {
356  head = idxMap[fBuff->fSegs[3*seg[0] + 1]];
357  tail = idxMap[fBuff->fSegs[3*seg[0] + 2]];
358  }
359  else
360  {
361  head = idxMap[fBuff->fSegs[3*seg[0] + 2]];
362  tail = idxMap[fBuff->fSegs[3*seg[0] + 1]];
363  }
364  pp.emplace_back(head);
365  // printf("start idx head %d, tail %d\n", head, tail);
366  LSeg_t segs;
367  for (UInt_t s = 1; s < segN; ++s)
368  segs.emplace_back(fBuff->fSegs[3*seg[s] + 1],fBuff->fSegs[3*seg[s] + 2]);
369 
370  for (auto &it: segs)
371  {
372  Int_t mv1 = idxMap[it.fV1];
373  Int_t mv2 = idxMap[it.fV2];
374 
375  if ( ! projection->AcceptSegment(fPnts[mv1], fPnts[mv2], REveProjection::fgEps))
376  {
377  pp.clear();
378  break;
379  }
380  if (tail != pp.back()) pp.push_back(tail);
381  tail = (mv1 == tail) ? mv2 : mv1;
382  }
383 
384  if ( ! pp.empty())
385  {
386  // DirectDraw() implementation: last and first vertices should not be equal
387  if (pp.front() == pp.back()) pp.pop_front();
388  surf += AddPolygon(pp, fPolsBP);
389  }
390  bpols += (segN+2);
391  }
392  return surf;
393 }
394 
395 ////////////////////////////////////////////////////////////////////////////////
396 /// Build polygons from the set of buffer segments.
397 /// First creates a segment pool according to reduced and projected points
398 /// and then build polygons from the pool.
399 
400 Float_t REvePolygonSetProjected::MakePolygonsFromBS(std::vector<Int_t> &idxMap)
401 {
402  LSeg_t segs;
403  Float_t surf = 0; // surface of projected polygons
404  REveProjection *projection = fManager->GetProjection();
405  for (UInt_t s = 0; s < fBuff->NbSegs(); ++s)
406  {
407  Bool_t duplicate = kFALSE;
408  Int_t vo1, vo2; // idx from fBuff segment
409  Int_t vor1, vor2; // mapped idx
410  vo1 = fBuff->fSegs[3*s + 1];
411  vo2 = fBuff->fSegs[3*s + 2]; //... skip color info
412  vor1 = idxMap[vo1];
413  vor2 = idxMap[vo2];
414  if (vor1 == vor2) continue;
415  // check duplicate
416  for (auto &seg: segs)
417  {
418  Int_t vv1 = seg.fV1;
419  Int_t vv2 = seg.fV2;
420  if((vv1 == vor1 && vv2 == vor2) || (vv1 == vor2 && vv2 == vor1))
421  {
422  duplicate = kTRUE;
423  continue;
424  }
425  }
426  if (duplicate == kFALSE && projection->AcceptSegment(fPnts[vor1], fPnts[vor2], REveProjection::fgEps))
427  segs.emplace_back(vor1, vor2);
428  }
429 
430  while (!segs.empty())
431  {
432  std::list<Int_t> pp; // points in current polygon
433  pp.push_back(segs.front().fV1);
434  Int_t tail = segs.front().fV2;
435  segs.pop_front();
436  Bool_t match = kTRUE;
437  while (match && ! segs.empty())
438  {
439  for (auto k = segs.begin(); k != segs.end(); ++k)
440  {
441  Int_t cv1 = (*k).fV1;
442  Int_t cv2 = (*k).fV2;
443  if (cv1 == tail || cv2 == tail)
444  {
445  pp.emplace_back(tail);
446  tail = (cv1 == tail) ? cv2 : cv1;
447  segs.erase(k);
448  match = kTRUE;
449  break;
450  }
451  else
452  {
453  match = kFALSE;
454  }
455  } // end for loop in the segment pool
456  if (tail == pp.front())
457  break;
458  }
459  surf += AddPolygon(pp, fPolsBS);
460  }
461  return surf;
462 }
463 
464 ////////////////////////////////////////////////////////////////////////////////
465 /// Project current buffer.
466 
467 void REvePolygonSetProjected::ProjectBuffer3D()
468 {
469  // create map from original to projected and reduced point needed only for geometry
470  auto idxMap = ProjectAndReducePoints();
471 
472  REveProjection::EGeoMode_e mode = fManager->GetProjection()->GetGeoMode();
473  switch (mode) {
474  case REveProjection::kGM_Polygons: {
475  MakePolygonsFromBP(idxMap);
476  fPolsBP.swap(fPols);
477  break;
478  }
479  case REveProjection::kGM_Segments: {
480  MakePolygonsFromBS(idxMap);
481  fPolsBS.swap(fPols);
482  break;
483  }
484  case REveProjection::kGM_Unknown: {
485  // take projection with largest surface
486  Float_t surfBP = MakePolygonsFromBP(idxMap);
487  Float_t surfBS = MakePolygonsFromBS(idxMap);
488  if (surfBS < surfBP) {
489  fPolsBP.swap(fPols);
490  fPolsBS.clear();
491  } else {
492  fPolsBS.swap(fPols);
493  fPolsBP.clear();
494  }
495  break;
496  }
497  default: break;
498  }
499 
500  ResetBBox();
501 }
502 
503 ////////////////////////////////////////////////////////////////////////////////
504 /// Calculate XY surface of a polygon.
505 
506 Float_t REvePolygonSetProjected::PolygonSurfaceXY(const REvePolygonSetProjected::Polygon_t &p) const
507 {
508  Float_t surf = 0;
509  Int_t nPnts = p.NPoints();
510  for (Int_t i = 0; i < nPnts - 1; ++i)
511  {
512  Int_t a = p.fPnts[i];
513  Int_t b = p.fPnts[i+1];
514  surf += fPnts[a].fX * fPnts[b].fY - fPnts[a].fY * fPnts[b].fX;
515  }
516  return 0.5f * TMath::Abs(surf);
517 }
518 
519 ////////////////////////////////////////////////////////////////////////////////
520 /// Dump information about built polygons.
521 
522 void REvePolygonSetProjected::DumpPolys() const
523 {
524  printf("REvePolygonSetProjected %d polygons\n", (Int_t)fPols.size());
525  Int_t cnt = 0;
526  for ( auto &pol : fPols)
527  {
528  Int_t nPnts = pol.NPoints();
529  printf("Points of polygon %d [Np = %d]:\n", ++cnt, nPnts);
530  for (Int_t vi = 0; vi<nPnts; ++vi) {
531  Int_t pi = pol.fPnts[vi];
532  printf(" (%f, %f, %f)", fPnts[pi].fX, fPnts[pi].fY, fPnts[pi].fZ);
533  }
534  printf(", surf=%f\n", PolygonSurfaceXY(pol));
535  }
536 }
537 
538 ////////////////////////////////////////////////////////////////////////////////
539 /// Dump information about currently projected buffer.
540 
541 void REvePolygonSetProjected::DumpBuffer3D()
542 {
543  Int_t* bpols = fBuff->fPols;
544 
545  for (UInt_t pi = 0; pi< fBuff->NbPols(); ++pi)
546  {
547  UInt_t segN = bpols[1];
548  printf("%d polygon of %d has %d segments \n", pi, fBuff->NbPols(), segN);
549 
550  Int_t* seg = &bpols[2];
551  for (UInt_t a=0; a<segN; ++a)
552  {
553  Int_t a1 = fBuff->fSegs[3*seg[a] + 1];
554  Int_t a2 = fBuff->fSegs[3*seg[a] + 2];
555  printf("(%d, %d) \n", a1, a2);
556  printf("ORIG points :(%f, %f, %f) (%f, %f, %f)\n",
557  fBuff->fPnts[3*a1],fBuff->fPnts[3*a1+1], fBuff->fPnts[3*a1+2],
558  fBuff->fPnts[3*a2],fBuff->fPnts[3*a2+1], fBuff->fPnts[3*a2+2]);
559  }
560  printf("\n");
561  bpols += (segN+2);
562  }
563 }