Logo ROOT  
Reference Guide
TGeoTessellated.cxx
Go to the documentation of this file.
1// @(#)root/geom:$Id$// Author: Andrei Gheata 24/10/01
2
3// Contains() and DistFromOutside/Out() implemented by Mihaela Gheata
4
5/*************************************************************************
6 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. *
7 * All rights reserved. *
8 * *
9 * For the licensing terms see $ROOTSYS/LICENSE. *
10 * For the list of contributors see $ROOTSYS/README/CREDITS. *
11 *************************************************************************/
12
13/** \class TGeoTessellated
14\ingroup Geometry_classes
15
16Tessellated solid class. It is composed by a set of planar faces having triangular or
17quadrilateral shape. The class does not provide navigation functionality, it just wraps the data
18for the composing faces.
19*/
20
21#include "Riostream.h"
22#include <sstream>
23
24#include "TGeoManager.h"
25#include "TGeoMatrix.h"
26#include "TGeoVolume.h"
27#include "TVirtualGeoPainter.h"
28#include "TGeoTessellated.h"
29#include "TVirtualPad.h"
30#include "TBuffer3D.h"
31#include "TBuffer3DTypes.h"
32#include "TMath.h"
33#include "TRandom.h"
34
35#include <array>
36#include <vector>
37
39
41
42std::ostream &operator<<(std::ostream &os, TGeoFacet const &facet)
43{
44 os << "{";
45 for (int i = 0; i < facet.GetNvert(); ++i) {
46 os << facet.GetVertex(i);
47 if (i != facet.GetNvert() - 1)
48 os << ", ";
49 }
50 os << "}";
51 return os;
52}
53
54TGeoFacet::TGeoFacet(const TGeoFacet &other) : fVertices(other.fVertices), fNvert(other.fNvert), fShared(other.fShared)
55{
56 memcpy(fIvert, other.fIvert, 4 * sizeof(int));
57 if (!fShared && other.fVertices)
58 fVertices = new VertexVec_t(*other.fVertices);
59}
60
62{
63 if (&other != this) {
64 if (!fShared)
65 delete fVertices;
66 fNvert = other.fNvert;
67 fShared = other.fShared;
68 memcpy(fIvert, other.fIvert, 4 * sizeof(int));
69 if (!fShared && other.fVertices)
70 fVertices = new VertexVec_t(*other.fVertices);
71 else
72 fVertices = other.fVertices;
73 }
74 return *this;
75}
76
77Vertex_t TGeoFacet::ComputeNormal(bool &degenerated) const
78{
79 // Compute normal using non-zero segments
80 constexpr double kTolerance = 1.e-20;
81 degenerated = true;
82 Vertex_t normal;
83 for (int i = 0; i < fNvert - 1; ++i) {
84 Vertex_t e1 = GetVertex(i + 1) - GetVertex(i);
85 if (e1.Mag2() < kTolerance)
86 continue;
87 for (int j = i + 1; j < fNvert; ++j) {
88 Vertex_t e2 = GetVertex((j + 1) % fNvert) - GetVertex(j);
89 if (e2.Mag2() < kTolerance)
90 continue;
91 normal = Vertex_t::Cross(e1, e2);
92 // e1 and e2 may be colinear
93 if (normal.Mag2() < kTolerance)
94 continue;
95 normal.Normalize();
96 degenerated = false;
97 break;
98 }
99 if (!degenerated)
100 break;
101 }
102 return normal;
103}
104
106{
107 constexpr double kTolerance = 1.e-10;
108 bool degenerated = true;
109 ComputeNormal(degenerated);
110 if (degenerated) {
111 std::cout << "Facet: " << *this << " is degenerated\n";
112 return false;
113 }
114
115 // Compute surface area
116 double surfaceArea = 0.;
117 for (int i = 1; i < fNvert - 1; ++i) {
118 Vertex_t e1 = GetVertex(i) - GetVertex(0);
119 Vertex_t e2 = GetVertex(i + 1) - GetVertex(0);
120 surfaceArea += 0.5 * Vertex_t::Cross(e1, e2).Mag();
121 }
122 if (surfaceArea < kTolerance) {
123 std::cout << "Facet: " << *this << " has zero surface area\n";
124 return false;
125 }
126
127 // Center of the tile
128 /*
129 Vertex_t center;
130 for (int i = 0; i < fNvert; ++i)
131 center += GetVertex(i);
132 center /= fNvert;
133 */
134 return true;
135}
136
137int TGeoFacet::CompactFacet(Vertex_t *vert, int nvertices)
138{
139 // Compact the common vertices and return new facet
140 if (nvertices < 2)
141 return nvertices;
142 int nvert = nvertices;
143 int i = 0;
144 while (i < nvert) {
145 if (vert[(i + 1) % nvert] == vert[i]) {
146 // shift last vertices left by one element
147 for (int j = i + 2; j < nvert; ++j)
148 vert[j - 1] = vert[j];
149 nvert--;
150 }
151 i++;
152 }
153 return nvert;
154}
155
156////////////////////////////////////////////////////////////////////////////////
157/// Check if a connected neighbour facet has compatible normal
158
159bool TGeoFacet::IsNeighbour(const TGeoFacet &other, bool &flip) const
160{
161
162 // Find a connecting segment
163 bool neighbour = false;
164 int line1[2], line2[2];
165 int npoints = 0;
166 for (int i = 0; i < fNvert; ++i) {
167 auto ivert = GetVertexIndex(i);
168 // Check if the other facet has the same vertex
169 for (int j = 0; j < other.GetNvert(); ++j) {
170 if (ivert == other.GetVertexIndex(j)) {
171 line1[npoints] = i;
172 line2[npoints] = j;
173 if (++npoints == 2) {
174 neighbour = true;
175 bool order1 = line1[1] == line1[0] + 1;
176 bool order2 = line2[1] == (line2[0] + 1) % other.GetNvert();
177 flip = (order1 == order2);
178 return neighbour;
179 }
180 }
181 }
182 }
183 return neighbour;
184}
185
186////////////////////////////////////////////////////////////////////////////////
187/// Constructor. In case nfacets is zero, it is user's responsibility to
188/// call CloseShape once all faces are defined.
189
190TGeoTessellated::TGeoTessellated(const char *name, int nfacets) : TGeoBBox(name, 0, 0, 0)
191{
192 fNfacets = nfacets;
193 if (nfacets)
194 fFacets.reserve(nfacets);
195}
196
197////////////////////////////////////////////////////////////////////////////////
198/// Constructor providing directly the array of vertices. Facets have to be added
199/// providing vertex indices rather than coordinates.
200
201TGeoTessellated::TGeoTessellated(const char *name, const std::vector<Vertex_t> &vertices) : TGeoBBox(name, 0, 0, 0)
202{
203 fVertices = vertices;
204 fNvert = fVertices.size();
205}
206
207////////////////////////////////////////////////////////////////////////////////
208/// Function to be called after reading tessellated volumes from the geometry file
209
211{
212 // The pointer to the array of vertices is not streamed so update it to facets
213 for (auto facet : fFacets)
214 facet.SetVertices(&fVertices);
215 fDefined = true;
216}
217
218////////////////////////////////////////////////////////////////////////////////
219/// Adding a triangular facet from vertex positions in absolute coordinates
220
221bool TGeoTessellated::AddFacet(const Vertex_t &pt0, const Vertex_t &pt1, const Vertex_t &pt2)
222{
223 if (fDefined) {
224 Error("AddFacet", "Shape %s already fully defined. Not adding", GetName());
225 return false;
226 }
227
228 Vertex_t vert[3];
229 vert[0] = pt0;
230 vert[1] = pt1;
231 vert[2] = pt2;
232 int nvert = TGeoFacet::CompactFacet(vert, 3);
233 if (nvert < 3) {
234 Error("AddFacet", "Triangular facet at index %d degenerated. Not adding.", GetNfacets());
235 return false;
236 }
237 fNvert += 3;
238 fNseg += 3;
239 fFacets.emplace_back(pt0, pt1, pt2);
240
241 if (fNfacets > 0 && GetNfacets() == fNfacets)
242 CloseShape();
243 return true;
244}
245
246////////////////////////////////////////////////////////////////////////////////
247/// Adding a triangular facet from indices of vertices
248
249bool TGeoTessellated::AddFacet(int i0, int i1, int i2)
250{
251 if (fDefined) {
252 Error("AddFacet", "Shape %s already fully defined. Not adding", GetName());
253 return false;
254 }
255 if (fVertices.size() == 0) {
256 Error("AddFacet", "Shape %s Cannot add facets by indices without vertices. Not adding", GetName());
257 return false;
258 }
259
260 fNseg += 3;
261 fFacets.emplace_back(&fVertices, 3, i0, i1, i2);
262 return true;
263}
264
265////////////////////////////////////////////////////////////////////////////////
266/// Adding a quadrilateral facet from vertex positions in absolute coordinates
267
268bool TGeoTessellated::AddFacet(const Vertex_t &pt0, const Vertex_t &pt1, const Vertex_t &pt2, const Vertex_t &pt3)
269{
270 if (fDefined) {
271 Error("AddFacet", "Shape %s already fully defined. Not adding", GetName());
272 return false;
273 }
274 Vertex_t vert[4];
275 vert[0] = pt0;
276 vert[1] = pt1;
277 vert[2] = pt2;
278 vert[3] = pt3;
279 int nvert = TGeoFacet::CompactFacet(vert, 4);
280 if (nvert < 3) {
281 Error("AddFacet", "Quadrilateral facet at index %d degenerated. Not adding.", GetNfacets());
282 return false;
283 }
284
285 fNvert += nvert;
286 fNseg += nvert;
287 if (nvert == 3)
288 fFacets.emplace_back(vert[0], vert[1], vert[2]);
289 else
290 fFacets.emplace_back(vert[0], vert[1], vert[2], vert[3]);
291
292 if (fNfacets > 0 && GetNfacets() == fNfacets)
293 CloseShape(false);
294 return true;
295}
296
297////////////////////////////////////////////////////////////////////////////////
298/// Adding a quadrilateral facet from indices of vertices
299
300bool TGeoTessellated::AddFacet(int i0, int i1, int i2, int i3)
301{
302 if (fDefined) {
303 Error("AddFacet", "Shape %s already fully defined. Not adding", GetName());
304 return false;
305 }
306 if (fVertices.size() == 0) {
307 Error("AddFacet", "Shape %s Cannot add facets by indices without vertices. Not adding", GetName());
308 return false;
309 }
310
311 fNseg += 4;
312 fFacets.emplace_back(&fVertices, 4, i0, i1, i2, i3);
313 return true;
314}
315
316////////////////////////////////////////////////////////////////////////////////
317/// Close the shape: calculate bounding box and compact vertices
318
319void TGeoTessellated::CloseShape(bool check, bool fixFlipped, bool verbose)
320{
321 // Compact the array of vertices
322 constexpr double tolerance = 1.e-10;
323 constexpr int ngrid = 100;
324 ComputeBBox();
325 double minExtent[3];
326 minExtent[0] = fOrigin[0] - fDX - tolerance;
327 minExtent[1] = fOrigin[1] - fDY - tolerance;
328 minExtent[2] = fOrigin[2] - fDZ - tolerance;
329
330 double invExtent[3];
331 invExtent[0] = 0.5 / (fDX + tolerance);
332 invExtent[1] = 0.5 / (fDY + tolerance);
333 invExtent[2] = 0.5 / (fDZ + tolerance);
334
335 if (fVertices.size() > 0) {
336 fDefined = true;
337 if (!check)
338 return;
339
340 // Check facets
341 for (auto &facet : fFacets) {
342 facet.Check();
343 }
344 fClosedBody = CheckClosure(fixFlipped, verbose);
345 return;
346 }
347
348 auto AddVertex = [this](const Vertex_t &vertex) {
349 // Check if vertex exists
350 int ivert = 0;
351 for (const auto &current_vert : fVertices) {
352 if (current_vert == vertex)
353 return ivert;
354 ivert++;
355 }
356 // Vertex new, just add it
357 fVertices.push_back(vertex);
358 return ivert;
359 };
360
361 auto GetHashIndex = [&](const Vertex_t &vertex) {
362 // Get the hash index for a vertex in a 10x10x10 grid in the bounding box
363 int index = 0;
364 for (int i = 0; i < 3; ++i) {
365 int ind = int(ngrid * (vertex[i] - minExtent[i]) * invExtent[i]); // between [0, ngrid-1]
366 assert(ind < (int)ngrid);
367 for (int j = i + 1; j < 3; ++j)
368 ind *= ngrid;
369 index += ind;
370 }
371 return index;
372 };
373
374 // In case the number of vertices is small, just compare with all others
375 int ind[4] = {-1, -1, -1, -1};
376 if (fNvert < 1000) {
377 for (auto &facet : fFacets) {
378 int nvert = facet.GetNvert();
379 for (int i = 0; i < nvert; ++i) {
380 // Check if vertex exists already
381 ind[i] = AddVertex(facet.GetVertex(i));
382 }
383 facet.SetVertices(&fVertices, nvert, ind[0], ind[1], ind[2], ind[3]);
384 }
385 } else {
386 // Use hash index for each vertex
387 using CellVec_t = std::vector<int>;
388 auto grid = new std::array<CellVec_t, ngrid * ngrid * ngrid>;
389 for (auto &facet : fFacets) {
390 int nvert = facet.GetNvert();
391 for (int i = 0; i < nvert; ++i) {
392 // Check if vertex exists already
393 const Vertex_t &vertex = facet.GetVertex(i);
394 int hashind = GetHashIndex(vertex);
395 bool isAdded = false;
396 for (auto ivert : grid->operator[](hashind)) {
397 if (vertex == fVertices[ivert]) {
398 ind[i] = ivert;
399 isAdded = true;
400 break;
401 }
402 }
403 if (!isAdded) {
404 fVertices.push_back(vertex);
405 ind[i] = fVertices.size() - 1;
406 grid->operator[](hashind).push_back(ind[i]);
407 }
408 }
409 facet.SetVertices(&fVertices, nvert, ind[0], ind[1], ind[2], ind[3]);
410 }
411 delete grid;
412 }
413 fNvert = fVertices.size();
414 fNfacets = fFacets.size();
415 fDefined = true;
416 if (!check)
417 return;
418
419 // Check facets
420 for (auto &facet : fFacets) {
421 facet.Check();
422 }
423
424 fClosedBody = CheckClosure(fixFlipped, verbose);
425}
426
427////////////////////////////////////////////////////////////////////////////////
428/// Check closure of the solid and check/fix flipped normals
429
430bool TGeoTessellated::CheckClosure(bool fixFlipped, bool verbose)
431{
432 int *nn = new int[fNfacets];
433 bool *flipped = new bool[fNfacets];
434 bool hasorphans = false;
435 bool hasflipped = false;
436 for (int i = 0; i < fNfacets; ++i) {
437 nn[i] = 0;
438 flipped[i] = false;
439 }
440
441 for (int icrt = 0; icrt < fNfacets; ++icrt) {
442 // all neighbours checked?
443 if (nn[icrt] >= fFacets[icrt].GetNvert())
444 continue;
445 for (int i = icrt + 1; i < fNfacets; ++i) {
446 bool isneighbour = fFacets[icrt].IsNeighbour(fFacets[i], flipped[i]);
447 if (isneighbour) {
448 if (flipped[icrt])
449 flipped[i] = !flipped[i];
450 if (flipped[i])
451 hasflipped = true;
452 nn[icrt]++;
453 nn[i]++;
454 if (nn[icrt] == fFacets[icrt].GetNvert())
455 break;
456 }
457 }
458 if (nn[icrt] < fFacets[icrt].GetNvert())
459 hasorphans = true;
460 }
461
462 if (hasorphans && verbose) {
463 Error("Check", "Tessellated solid %s has following not fully connected facets:", GetName());
464 for (int icrt = 0; icrt < fNfacets; ++icrt) {
465 if (nn[icrt] < fFacets[icrt].GetNvert())
466 std::cout << icrt << " (" << fFacets[icrt].GetNvert() << " edges, " << nn[icrt] << " neighbours)\n";
467 }
468 }
469 fClosedBody = !hasorphans;
470 int nfixed = 0;
471 if (hasflipped) {
472 if (verbose)
473 Warning("Check", "Tessellated solid %s has following facets with flipped normals:", GetName());
474 for (int icrt = 0; icrt < fNfacets; ++icrt) {
475 if (flipped[icrt]) {
476 if (verbose)
477 std::cout << icrt << "\n";
478 if (fixFlipped) {
479 fFacets[icrt].Flip();
480 nfixed++;
481 }
482 }
483 }
484 if (nfixed && verbose)
485 Info("Check", "Automatically flipped %d facets to match first defined facet", nfixed);
486 }
487 delete[] nn;
488 delete[] flipped;
489
490 return !hasorphans;
491}
492
493////////////////////////////////////////////////////////////////////////////////
494/// Compute bounding box
495
497{
498 const double kBig = TGeoShape::Big();
499 double vmin[3] = {kBig, kBig, kBig};
500 double vmax[3] = {-kBig, -kBig, -kBig};
501 for (const auto &facet : fFacets) {
502 for (int i = 0; i < facet.GetNvert(); ++i) {
503 for (int j = 0; j < 3; ++j) {
504 vmin[j] = TMath::Min(vmin[j], facet.GetVertex(i).operator[](j));
505 vmax[j] = TMath::Max(vmax[j], facet.GetVertex(i).operator[](j));
506 }
507 }
508 }
509 fDX = 0.5 * (vmax[0] - vmin[0]);
510 fDY = 0.5 * (vmax[1] - vmin[1]);
511 fDZ = 0.5 * (vmax[2] - vmin[2]);
512 for (int i = 0; i < 3; ++i)
513 fOrigin[i] = 0.5 * (vmax[i] + vmin[i]);
514}
515
516////////////////////////////////////////////////////////////////////////////////
517/// Returns numbers of vertices, segments and polygons composing the shape mesh.
518
519void TGeoTessellated::GetMeshNumbers(int &nvert, int &nsegs, int &npols) const
520{
521 nvert = fNvert;
522 nsegs = fNseg;
523 npols = GetNfacets();
524}
525
526////////////////////////////////////////////////////////////////////////////////
527/// Creates a TBuffer3D describing *this* shape.
528/// Coordinates are in local reference frame.
529
531{
532 const int nvert = fNvert;
533 const int nsegs = fNseg;
534 const int npols = GetNfacets();
535 auto buff = new TBuffer3D(TBuffer3DTypes::kGeneric, nvert, 3 * nvert, nsegs, 3 * nsegs, npols, 6 * npols);
536 if (buff) {
537 SetPoints(buff->fPnts);
538 SetSegsAndPols(*buff);
539 }
540 return buff;
541}
542
543////////////////////////////////////////////////////////////////////////////////
544/// Prints basic info
545
547{
548 std::cout << "=== Tessellated shape " << GetName() << " having " << GetNvertices() << " vertices and "
549 << GetNfacets() << " facets\n";
550}
551
552////////////////////////////////////////////////////////////////////////////////
553/// Fills TBuffer3D structure for segments and polygons.
554
556{
557 const int c = GetBasicColor();
558 int *segs = buff.fSegs;
559 int *pols = buff.fPols;
560
561 int indseg = 0; // segment internal data index
562 int indpol = 0; // polygon internal data index
563 int sind = 0; // segment index
564 for (const auto &facet : fFacets) {
565 auto nvert = facet.GetNvert();
566 pols[indpol++] = c;
567 pols[indpol++] = nvert;
568 for (auto j = 0; j < nvert; ++j) {
569 int k = (j + 1) % nvert;
570 // segment made by next consecutive points
571 segs[indseg++] = c;
572 segs[indseg++] = facet.GetVertexIndex(j);
573 segs[indseg++] = facet.GetVertexIndex(k);
574 // add segment to current polygon and increment segment index
575 pols[indpol + nvert - j - 1] = sind++;
576 }
577 indpol += nvert;
578 }
579}
580
581////////////////////////////////////////////////////////////////////////////////
582/// Fill tessellated points to an array.
583
585{
586 int ind = 0;
587 for (const auto &vertex : fVertices) {
588 vertex.CopyTo(&points[ind]);
589 ind += 3;
590 }
591}
592
593////////////////////////////////////////////////////////////////////////////////
594/// Fill tessellated points in float.
595
597{
598 int ind = 0;
599 for (const auto &vertex : fVertices) {
600 points[ind++] = vertex.x();
601 points[ind++] = vertex.y();
602 points[ind++] = vertex.z();
603 }
604}
605
606////////////////////////////////////////////////////////////////////////////////
607/// Resize the shape by scaling vertices within maxsize and center to origin
608
610{
611 using Vector3_t = Vertex_t;
612
613 if (!fDefined) {
614 Error("ResizeCenter", "Not all faces are defined");
615 return;
616 }
617 Vector3_t origin(fOrigin[0], fOrigin[1], fOrigin[2]);
618 double maxedge = TMath::Max(TMath::Max(fDX, fDY), fDZ);
619 double scale = maxsize / maxedge;
620 for (size_t i = 0; i < fVertices.size(); ++i) {
621 fVertices[i] = scale * (fVertices[i] - origin);
622 }
623 fOrigin[0] = fOrigin[1] = fOrigin[2] = 0;
624 fDX *= scale;
625 fDY *= scale;
626 fDZ *= scale;
627}
628
629////////////////////////////////////////////////////////////////////////////////
630/// Fills a static 3D buffer and returns a reference.
631
632const TBuffer3D &TGeoTessellated::GetBuffer3D(int reqSections, Bool_t localFrame) const
633{
634 static TBuffer3D buffer(TBuffer3DTypes::kGeneric);
635
636 FillBuffer3D(buffer, reqSections, localFrame);
637
638 const int nvert = fNvert;
639 const int nsegs = fNseg;
640 const int npols = GetNfacets();
641
642 if (reqSections & TBuffer3D::kRawSizes) {
643 if (buffer.SetRawSizes(nvert, 3 * nvert, nsegs, 3 * nsegs, npols, 6 * npols)) {
645 }
646 }
647 if ((reqSections & TBuffer3D::kRaw) && buffer.SectionsValid(TBuffer3D::kRawSizes)) {
648 SetPoints(buffer.fPnts);
649 if (!buffer.fLocalFrame) {
650 TransformPoints(buffer.fPnts, buffer.NbPnts());
651 }
652
653 SetSegsAndPols(buffer);
655 }
656
657 return buffer;
658}
659
660////////////////////////////////////////////////////////////////////////////////
661/// Reads a single tessellated solid from an .obj file.
662
663TGeoTessellated *TGeoTessellated::ImportFromObjFormat(const char *objfile, bool check, bool verbose)
664{
665 using namespace std;
666
667 vector<Vertex_t> vertices;
668 vector<string> sfacets;
669
670 struct FacetInd_t {
671 int i0 = -1;
672 int i1 = -1;
673 int i2 = -1;
674 int i3 = -1;
675 int nvert = 0;
676 FacetInd_t(int a, int b, int c)
677 {
678 i0 = a;
679 i1 = b;
680 i2 = c;
681 nvert = 3;
682 };
683 FacetInd_t(int a, int b, int c, int d)
684 {
685 i0 = a;
686 i1 = b;
687 i2 = c;
688 i3 = d;
689 nvert = 4;
690 };
691 };
692
693 vector<FacetInd_t> facets;
694 // List of geometric vertices, with (x, y, z [,w]) coordinates, w is optional and defaults to 1.0.
695 // struct vtx_t { double x = 0; double y = 0; double z = 0; double w = 1; };
696
697 // Texture coordinates in u, [,v ,w]) coordinates, these will vary between 0 and 1. v, w are optional and default to
698 // 0.
699 // struct tex_t { double u; double v; double w; };
700
701 // List of vertex normals in (x,y,z) form; normals might not be unit vectors.
702 // struct vn_t { double x; double y; double z; };
703
704 // Parameter space vertices in ( u [,v] [,w] ) form; free form geometry statement
705 // struct vp_t { double u; double v; double w; };
706
707 // Faces are defined using lists of vertex, texture and normal indices which start at 1.
708 // Polygons such as quadrilaterals can be defined by using more than three vertex/texture/normal indices.
709 // f v1//vn1 v2//vn2 v3//vn3 ...
710
711 // Records starting with the letter "l" specify the order of the vertices which build a polyline.
712 // l v1 v2 v3 v4 v5 v6 ...
713
714 string line;
715 int ind[4] = {0};
716 ifstream file(objfile);
717 if (!file.is_open()) {
718 ::Error("TGeoTessellated::ImportFromObjFormat", "Unable to open %s", objfile);
719 return nullptr;
720 }
721
722 while (getline(file, line)) {
723 stringstream ss(line);
724 string tag;
725
726 // We ignore everything which is not a vertex or a face
727 if (line.rfind("v", 0) == 0 && line.rfind("vt", 0) != 0 && line.rfind("vn", 0) != 0 && line.rfind("vn", 0) != 0) {
728 // Decode the vertex
729 double pos[4] = {0, 0, 0, 1};
730 ss >> tag >> pos[0] >> pos[1] >> pos[2] >> pos[3];
731 vertices.emplace_back(pos[0] * pos[3], pos[1] * pos[3], pos[2] * pos[3]);
732 }
733
734 else if (line.rfind("f", 0) == 0) {
735 // Decode the face
736 ss >> tag;
737 string word;
738 sfacets.clear();
739 while (ss >> word)
740 sfacets.push_back(word);
741 if (sfacets.size() > 4 || sfacets.size() < 3) {
742 ::Error("TGeoTessellated::ImportFromObjFormat", "Detected face having unsupported %zu vertices",
743 sfacets.size());
744 return nullptr;
745 }
746 int nvert = 0;
747 for (auto sword : sfacets) {
748 stringstream ssword(sword);
749 string token;
750 getline(ssword, token, '/'); // just need the vertex index, which is the first token
751 // Convert string token to integer
752
753 ind[nvert++] = stoi(token) - 1;
754 if (ind[nvert - 1] < 0) {
755 ::Error("TGeoTessellated::ImportFromObjFormat", "Unsupported relative vertex index definition in %s",
756 objfile);
757 return nullptr;
758 }
759 }
760 if (nvert == 3)
761 facets.emplace_back(ind[0], ind[1], ind[2]);
762 else
763 facets.emplace_back(ind[0], ind[1], ind[2], ind[3]);
764 }
765 }
766
767 int nvertices = (int)vertices.size();
768 int nfacets = (int)facets.size();
769 if (nfacets < 3) {
770 ::Error("TGeoTessellated::ImportFromObjFormat", "Not enough faces detected in %s", objfile);
771 return nullptr;
772 }
773
774 string sobjfile(objfile);
775 if (verbose)
776 std::cout << "Read " << nvertices << " vertices and " << nfacets << " facets from " << sobjfile << endl;
777
778 auto tsl = new TGeoTessellated(sobjfile.erase(sobjfile.find_last_of('.')).c_str(), vertices);
779
780 for (int i = 0; i < nfacets; ++i) {
781 auto facet = facets[i];
782 if (facet.nvert == 3)
783 tsl->AddFacet(facet.i0, facet.i1, facet.i2);
784 else
785 tsl->AddFacet(facet.i0, facet.i1, facet.i2, facet.i3);
786 }
787 tsl->CloseShape(check, true, verbose);
788 tsl->Print();
789 return tsl;
790}
#define d(i)
Definition: RSha256.hxx:102
#define b(i)
Definition: RSha256.hxx:100
#define c(i)
Definition: RSha256.hxx:101
float Float_t
Definition: RtypesCore.h:55
const char Option_t
Definition: RtypesCore.h:64
#define ClassImp(name)
Definition: Rtypes.h:361
TGLVector3 Cross(const TGLVector3 &v1, const TGLVector3 &v2)
Definition: TGLUtil.h:322
char name[80]
Definition: TGX11.cxx:109
segment * segs
Definition: X3DBuffer.c:23
point * points
Definition: X3DBuffer.c:22
Generic 3D primitive description class.
Definition: TBuffer3D.h:18
Int_t * fPols
Definition: TBuffer3D.h:114
UInt_t NbPnts() const
Definition: TBuffer3D.h:80
Bool_t SectionsValid(UInt_t mask) const
Definition: TBuffer3D.h:67
@ kRawSizes
Definition: TBuffer3D.h:53
void SetSectionsValid(UInt_t mask)
Definition: TBuffer3D.h:65
Int_t * fSegs
Definition: TBuffer3D.h:113
Bool_t fLocalFrame
Definition: TBuffer3D.h:90
Bool_t SetRawSizes(UInt_t reqPnts, UInt_t reqPntsCapacity, UInt_t reqSegs, UInt_t reqSegsCapacity, UInt_t reqPols, UInt_t reqPolsCapacity)
Set kRaw tessellation section of buffer with supplied sizes.
Definition: TBuffer3D.cxx:359
Double_t * fPnts
Definition: TBuffer3D.h:112
Double_t Mag() const
Definition: TGLUtil.h:297
Box class.
Definition: TGeoBBox.h:18
Double_t fDX
Definition: TGeoBBox.h:21
Double_t fOrigin[3]
Definition: TGeoBBox.h:24
Double_t fDY
Definition: TGeoBBox.h:22
Double_t fDZ
Definition: TGeoBBox.h:23
virtual void FillBuffer3D(TBuffer3D &buffer, Int_t reqSections, Bool_t localFrame) const
Fills the supplied buffer, with sections in desired frame See TBuffer3D.h for explanation of sections...
Definition: TGeoBBox.cxx:1033
Tessellated::VertexVec_t VertexVec_t
bool Check() const
bool IsNeighbour(const TGeoFacet &other, bool &flip) const
Check if a connected neighbour facet has compatible normal.
static int CompactFacet(Vertex_t *vert, int nvertices)
int GetVertexIndex(int ivert) const
Vertex_t & GetVertex(int ivert)
VertexVec_t * fVertices
int fNvert
array of vertices
int fIvert[4]
Vertex_t ComputeNormal(bool &degenerated) const
int GetNvert() const
const TGeoFacet & operator=(const TGeoFacet &other)
static Double_t Big()
Definition: TGeoShape.h:88
Int_t GetBasicColor() const
Get the basic color (0-7).
Definition: TGeoShape.cxx:673
void TransformPoints(Double_t *points, UInt_t NbPoints) const
Tranform a set of points (LocalToMaster)
Definition: TGeoShape.cxx:552
virtual const char * GetName() const
Get the shape name.
Definition: TGeoShape.cxx:248
Tessellated solid class.
void ResizeCenter(double maxsize)
Resize and center the shape in a box of size maxsize.
virtual void Print(Option_t *option="") const
Prints basic info.
virtual const TBuffer3D & GetBuffer3D(int reqSections, Bool_t localFrame) const
Fills a static 3D buffer and returns a reference.
virtual void SetSegsAndPols(TBuffer3D &buff) const
Fills TBuffer3D structure for segments and polygons.
bool CheckClosure(bool fixFlipped=true, bool verbose=true)
Check closure of the solid and check/fix flipped normals.
int GetNvertices() const
virtual void SetPoints(double *points) const
Fill tessellated points to an array.
virtual TBuffer3D * MakeBuffer3D() const
Creates a TBuffer3D describing this shape.
void CloseShape(bool check=true, bool fixFlipped=true, bool verbose=true)
Close the shape: calculate bounding box and compact vertices.
Tessellated::Vertex_t Vertex_t
std::vector< TGeoFacet > fFacets
static TGeoTessellated * ImportFromObjFormat(const char *objfile, bool check=false, bool verbose=false)
Reader from .obj format.
void ComputeBBox()
Compute bounding box.
virtual void AfterStreamer()
Function to be called after reading tessellated volumes from the geometry file.
bool AddFacet(const Vertex_t &pt0, const Vertex_t &pt1, const Vertex_t &pt2)
Adding a triangular facet from vertex positions in absolute coordinates.
virtual void GetMeshNumbers(int &nvert, int &nsegs, int &npols) const
Returns numbers of vertices, segments and polygons composing the shape mesh.
std::vector< Vertex_t > fVertices
int GetNfacets() const
bool fClosedBody
Shape fully defined.
virtual void Warning(const char *method, const char *msgfmt,...) const
Issue warning message.
Definition: TObject.cxx:877
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition: TObject.cxx:891
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition: TObject.cxx:865
TLine * line
static double kBig
Short_t Max(Short_t a, Short_t b)
Definition: TMathBase.h:212
Short_t Min(Short_t a, Short_t b)
Definition: TMathBase.h:180
Typedefs used by the geometry group.
Definition: TGeoTypedefs.h:15
Definition: file.py:1
static int AddVertex(GLUtesselator *tess, GLdouble coords[3], void *data)
Definition: tess.c:355
auto * a
Definition: textangle.C:12
REAL * vertex
Definition: triangle.c:512
void flip(struct mesh *m, struct behavior *b, struct otri *flipedge)
Definition: triangle.c:7889