VGM Version 5.5
Loading...
Searching...
No Matches
Arb8.cxx
Go to the documentation of this file.
1// $Id$
2
3// -----------------------------------------------------------------------
4// The Geant4GM package of the Virtual Geometry Model
5// Copyright (C) 2007, Ivana Hrivnacova
6// All rights reserved.
7//
8// For the licensing terms see vgm/LICENSE.
9// Contact: ivana@ipno.in2p3.fr
10// -----------------------------------------------------------------------
11
12//
13// Class Arb8
14// --------------------
15// VGM implementation for Geant4 Arb8 solid.
16//
17// Author: Ivana Hrivnacova; IPN Orsay
18
22
23#include "ClhepVGM/Units.h"
24
25#include "G4GenericTrap.hh"
26#include "G4MultiUnion.hh"
27#include "G4QuadrangularFacet.hh"
28#include "G4ReflectedSolid.hh"
29#include "G4TessellatedSolid.hh"
30#include "G4TriangularFacet.hh"
31
32#include <algorithm>
33#include <cmath>
34#include <iostream>
35
36const int Geant4GM::Arb8::fgkNofVertices = 8;
37const double Geant4GM::Arb8::fgkTolerance = 1E-3;
38// The largest twist G4GenericTrap accepts on a lateral face, in degrees.
39const double Geant4GM::Arb8::fgkMaxTwistAngle = 90.;
40
41//_____________________________________________________________________________
42double Geant4GM::Arb8::TwistAngleOfFace(
43 const std::vector<VGM::TwoVector>& vertices, int index)
44{
49
50 int nv = fgkNofVertices / 2;
51 int i = index % nv;
52 int j = (index + 1) % nv;
53
54 double dx1 = vertices[j].first - vertices[i].first;
55 double dy1 = vertices[j].second - vertices[i].second;
56 double dx2 = vertices[nv + j].first - vertices[nv + i].first;
57 double dy2 = vertices[nv + j].second - vertices[nv + i].second;
58
59 if ((dx1 == 0 && dy1 == 0) || (dx2 == 0 && dy2 == 0)) return 0.;
60
61 return atan2(dx1 * dy2 - dy1 * dx2, dx1 * dx2 + dy1 * dy2);
62}
63
64//_____________________________________________________________________________
66 const std::vector<VGM::TwoVector>& vertices)
67{
71
72 double maxAngle = 0.;
73 for (int i = 0; i < 4; i++) {
74 double angle = fabs(TwistAngleOfFace(vertices, i)) * 180. / M_PI;
75 if (angle > maxAngle) maxAngle = angle;
76 }
77 return maxAngle;
78}
79
80//_____________________________________________________________________________
81bool Geant4GM::Arb8::IsTwisted(std::vector<VGM::TwoVector> vertices)
82{
86
87 bool twisted = false;
88 double dx1, dy1, dx2, dy2;
89 int nv = fgkNofVertices / 2;
90 for (int i = 0; i < 4; i++) {
91
92 dx1 = vertices[(i + 1) % nv].first - vertices[i].first;
93 dy1 = vertices[(i + 1) % nv].second - vertices[i].second;
94 if (dx1 == 0 && dy1 == 0) continue;
95
96 dx2 = vertices[nv + (i + 1) % nv].first - vertices[nv + i].first;
97 dy2 = vertices[nv + (i + 1) % nv].second - vertices[nv + i].second;
98 if (dx2 == 0 && dy2 == 0) continue;
99
100 if (fabs(dy1 * dx2 - dx1 * dy2) < fgkTolerance) continue;
101
102 twisted = true;
103 }
104 return twisted;
105}
106
107//_____________________________________________________________________________
109 const std::string& name, double hz, std::vector<VGM::TwoVector> vertices)
110 : VGM::ISolid(),
111 VGM::IArb8(),
112 BaseVGM::VArb8(),
113 fHz(hz),
114 fVertices(vertices),
115 fTessellatedSolid(0),
116 fSolid(0)
117{
138
139 auto faceSpan = [&vertices](G4int first) {
140 double span = 0.;
141 for (G4int i = first + 1; i < first + 4; ++i) {
142 for (G4int j = first; j < i; ++j) {
143 double dx = vertices[i].first - vertices[j].first;
144 double dy = vertices[i].second - vertices[j].second;
145 span = std::max(span, std::sqrt(dx * dx + dy * dy));
146 }
147 }
148 return span;
149 };
150 double downSpan = faceSpan(0);
151 double upSpan = faceSpan(4);
152 bool hasCollapsedFace =
153 std::min(downSpan, upSpan) < 1.e-7 * std::max(downSpan, upSpan);
154
155 // A twisted arb8 has non-planar sides, so it cannot be built from planar
156 // facets. Near-collapsed end faces can also make tessellated-solid
157 // navigation ambiguous. G4GenericTrap handles both cases.
158 if (IsTwisted(vertices) || hasCollapsedFace) {
159 auto makeGenericTrap = [&name](const std::string& suffix, double halfLength,
160 const std::vector<VGM::TwoVector>& trapVertices) {
161 std::vector<G4TwoVector> g4Vertices;
162 g4Vertices.reserve(trapVertices.size());
163 for (const auto& vertex : trapVertices) {
164 g4Vertices.push_back(
165 G4TwoVector(vertex.first / ClhepVGM::Units::Length(),
166 vertex.second / ClhepVGM::Units::Length()));
167 }
168 return new G4GenericTrap(
169 name + suffix, halfLength / ClhepVGM::Units::Length(), g4Vertices);
170 };
171
172 double maxTwist = MaxTwistAngle(vertices);
173 if (maxTwist > fgkMaxTwistAngle) {
174 Arb8Split split;
175 Arb8SplitResult result = SplitArb8ForGenericTrap(hz, vertices, split);
176 if (result != Arb8SplitResult::kSuccess) {
177 std::cerr << "+++ Error +++" << std::endl;
178 std::cerr << " Arb8 \"" << name
179 << "\" cannot be divided into two G4GenericTrap solids."
180 << std::endl;
181 std::cerr << " Its maximum lateral-face twist is " << maxTwist
182 << " degrees." << std::endl;
183 exit(1);
184 }
185
186 G4GenericTrap* lower =
187 makeGenericTrap("_lower", split.lowerHalfLength, split.lowerVertices);
188 G4GenericTrap* upper =
189 makeGenericTrap("_upper", split.upperHalfLength, split.upperVertices);
190
191 G4MultiUnion* combined = new G4MultiUnion(name);
192 G4RotationMatrix rotation;
193 combined->AddNode(
194 *lower, G4Transform3D(
195 rotation, G4ThreeVector(0., 0.,
197 combined->AddNode(
198 *upper, G4Transform3D(
199 rotation, G4ThreeVector(0., 0.,
201 combined->Voxelize();
202 fSolid = combined;
203 }
204 else {
205 fSolid = makeGenericTrap("", hz, vertices);
206 }
207 Geant4GM::SolidMap::Instance()->AddSolid(this, fSolid);
208 return;
209 }
210
211 // 3D vertices
212 G4int nv = fgkNofVertices / 2;
213 std::vector<G4ThreeVector> downVertices;
214 for (G4int i = 0; i < nv; i++)
215 downVertices.push_back(
216 G4ThreeVector(vertices[i].first / ClhepVGM::Units::Length(),
217 vertices[i].second / ClhepVGM::Units::Length(),
218 -hz / ClhepVGM::Units::Length()));
219
220 std::vector<G4ThreeVector> upVertices;
221 for (G4int i = nv; i < 2 * nv; i++)
222 upVertices.push_back(
223 G4ThreeVector(vertices[i].first / ClhepVGM::Units::Length(),
224 vertices[i].second / ClhepVGM::Units::Length(),
226
227 // Reorder vertices if they are not ordered anti-clock wise
228 G4ThreeVector cross = (downVertices[1] - downVertices[0])
229 .cross(downVertices[2] - downVertices[1]);
230 if (cross.z() > 0.0) {
231 ReorderVertices(downVertices);
232 ReorderVertices(upVertices);
233 }
234
235 fTessellatedSolid = new G4TessellatedSolid(name);
236
237 G4VFacet* facet = 0;
238 facet = MakeDownFacet(downVertices, 0, 1, 2);
239 if (facet) fTessellatedSolid->AddFacet(facet);
240
241 facet = MakeDownFacet(downVertices, 0, 2, 3);
242 if (facet) fTessellatedSolid->AddFacet(facet);
243
244 facet = MakeUpFacet(upVertices, 0, 2, 1);
245 if (facet) fTessellatedSolid->AddFacet(facet);
246
247 facet = MakeUpFacet(upVertices, 0, 3, 2);
248 if (facet) fTessellatedSolid->AddFacet(facet);
249
250 // The quadrangular sides
251 for (G4int i = 0; i < nv; ++i) {
252 G4int j = (i + 1) % nv;
253 facet = MakeSideFacet(
254 downVertices[j], downVertices[i], upVertices[i], upVertices[j]);
255
256 if (facet) fTessellatedSolid->AddFacet(facet);
257 }
258
259 fTessellatedSolid->SetSolidClosed(true);
260
261 // G4cout << "Arb8 solid " << Name() << G4endl;
262 // G4cout << *fTessellatedSolid << G4endl;
263
264 fSolid = fTessellatedSolid;
265 Geant4GM::SolidMap::Instance()->AddSolid(this, fSolid);
266}
267
268//_____________________________________________________________________________
270 G4GenericTrap* genericTrap, G4ReflectedSolid* reflected)
271 : VGM::ISolid(),
272 VGM::IArb8(),
273 BaseVGM::VArb8(),
274 fHz(genericTrap->GetZHalfLength() * ClhepVGM::Units::Length()),
275 fVertices(),
276 fTessellatedSolid(0),
277 fSolid(genericTrap)
278{
280
281 const std::vector<G4TwoVector>& vertices = genericTrap->GetVertices();
282 fVertices.reserve(vertices.size());
283
284 for (G4int i = 0; i < G4int(vertices.size()); ++i) {
285 // Reflection in z exchanges the lower and upper vertex planes.
286 G4int source = reflected ? (i + fgkNofVertices / 2) % fgkNofVertices : i;
287 fVertices.push_back(VGM::TwoVector(
288 vertices[source].x() * ClhepVGM::Units::Length(),
289 vertices[source].y() * ClhepVGM::Units::Length()));
290 }
291
292 if (reflected)
293 Geant4GM::SolidMap::Instance()->AddSolid(this, reflected);
294 else
295 Geant4GM::SolidMap::Instance()->AddSolid(this, genericTrap);
296}
297
298//_____________________________________________________________________________
300 : VGM::ISolid(),
301 VGM::IArb8(),
302 BaseVGM::VArb8(),
303 fHz(0),
304 fVertices(),
305 fTessellatedSolid(0),
306 fSolid(0)
307{
309}
310
311//_____________________________________________________________________________
313 : VGM::ISolid(rhs),
314 VGM::IArb8(rhs),
315 BaseVGM::VArb8(rhs),
316 fHz(0),
317 fVertices(),
318 fTessellatedSolid(0),
319 fSolid(0)
320{
322}
323
324//_____________________________________________________________________________
326{
327 //
328}
329
330//_____________________________________________________________________________
331void Geant4GM::Arb8::ReorderVertices(std::vector<G4ThreeVector>& vertices)
332{
333 // Reorder the vector of vertices
334
335 std::vector<G4ThreeVector> oldVertices(vertices);
336
337 for (unsigned int i = 0; i < oldVertices.size(); ++i) {
338 vertices[i] = oldVertices[oldVertices.size() - 1 - i];
339 }
340}
341
342//_____________________________________________________________________________
343G4VFacet* Geant4GM::Arb8::MakeDownFacet(
344 std::vector<G4ThreeVector> fromVertices, int ind1, int ind2, int ind3) const
345{
346 // Create a triangular facet from the polygon points given by indices
347 // forming the down side ( the normal goes in -z)
348
349 // Do not create facet if 2 vertices are the same
350 if (fromVertices[ind1] == fromVertices[ind2] ||
351 fromVertices[ind2] == fromVertices[ind3] ||
352 fromVertices[ind1] == fromVertices[ind3])
353 return 0;
354
355 std::vector<G4ThreeVector> vertices;
356 vertices.push_back(fromVertices[ind1]);
357 vertices.push_back(fromVertices[ind2]);
358 vertices.push_back(fromVertices[ind3]);
359
360 // first vertex most left
361 //
362 G4ThreeVector cross =
363 (vertices[1] - vertices[0]).cross(vertices[2] - vertices[1]);
364
365 if (cross.z() > 0.0) {
366 // Should not happen, as vertices should have been reordered
367 // at this stage
368 std::cerr << " Geant4GM::Arb8::MakeDownFacet:" << std::endl;
369 std::cerr << " Vertices in wrong order." << std::endl;
370 std::cerr << "*** Error: Aborting execution ***" << std::endl;
371 exit(1);
372 }
373
374 return new G4TriangularFacet(vertices[0], vertices[1], vertices[2], ABSOLUTE);
375}
376
377//_____________________________________________________________________________
378G4VFacet* Geant4GM::Arb8::MakeUpFacet(
379 std::vector<G4ThreeVector> fromVertices, int ind1, int ind2, int ind3) const
380{
381 // Creates a triangular facet from the polygon points given by indices
382 // forming the upper side ( z>0 )
383
384 // Do not create facet if 2 vertices are the same
385 if (fromVertices[ind1] == fromVertices[ind2] ||
386 fromVertices[ind2] == fromVertices[ind3] ||
387 fromVertices[ind1] == fromVertices[ind3])
388 return 0;
389
390 std::vector<G4ThreeVector> vertices;
391 vertices.push_back(fromVertices[ind1]);
392 vertices.push_back(fromVertices[ind2]);
393 vertices.push_back(fromVertices[ind3]);
394
395 // first vertex most left
396 //
397 G4ThreeVector cross =
398 (vertices[1] - vertices[0]).cross(vertices[2] - vertices[1]);
399
400 if (cross.z() < 0.0) {
401 // Should not happen, as vertices should have been reordered
402 // at this stage
403 std::cerr << " Geant4GM::Arb8::MakeUpFacet:" << std::endl;
404 std::cerr << " Vertices in wrong order." << std::endl;
405 std::cerr << "*** Error: Aborting execution ***" << std::endl;
406 exit(1);
407 }
408
409 return new G4TriangularFacet(vertices[0], vertices[1], vertices[2], ABSOLUTE);
410}
411
412//_____________________________________________________________________________
413G4VFacet* Geant4GM::Arb8::MakeSideFacet(G4ThreeVector downVertex0,
414 G4ThreeVector downVertex1, G4ThreeVector upVertex1,
415 G4ThreeVector upVertex0) const
416{
417 // Creates a triangular facet from the polygon points given by indices
418 // forming the upper side ( z>0 )
419
420 if (downVertex0 == downVertex1 && upVertex0 == upVertex1) return 0;
421
422 if (downVertex0 == downVertex1)
423 return new G4TriangularFacet(downVertex0, upVertex1, upVertex0, ABSOLUTE);
424
425 if (upVertex0 == upVertex1)
426 return new G4TriangularFacet(downVertex0, downVertex1, upVertex0, ABSOLUTE);
427
428 return new G4QuadrangularFacet(
429 downVertex0, downVertex1, upVertex1, upVertex0, ABSOLUTE);
430}
431
432//_____________________________________________________________________________
433std::string Geant4GM::Arb8::Name() const
434{
435 return fSolid->GetName();
436}
437
438//_____________________________________________________________________________
439int Geant4GM::Arb8::NofVertices() const { return fgkNofVertices; }
440
441//_____________________________________________________________________________
443{
444 if (index < 0 || index >= NofVertices()) {
445 std::cerr << "+++ Error +++" << std::endl;
446 std::cerr << " Wrong vertex index: " << index << std::endl;
447 exit(1);
448 }
449
450 return fVertices[index];
451}
452
453//_____________________________________________________________________________
454double Geant4GM::Arb8::TwistAngle(int index) const
455{
456 if (index < 0 || index >= 4) {
457 std::cerr << "+++ Error +++" << std::endl;
458 std::cerr << " Wrong twist angle index: " << index << std::endl;
459 exit(1);
460 }
461
462 return TwistAngleOfFace(fVertices, index) * ClhepVGM::Units::Angle();
463}
464
465//_____________________________________________________________________________
466double Geant4GM::Arb8::ZHalfLength() const { return fHz; }
static double Length()
Return CLHEP default length unit in VGM units.
Definition Units.cxx:83
static double Angle()
Return CLHEP default angle unit in VGM units.
Definition Units.cxx:92
static bool IsTwisted(std::vector< VGM::TwoVector > vertices)
Definition Arb8.cxx:81
virtual ~Arb8()
Definition Arb8.cxx:325
virtual int NofVertices() const
Return the number of vertices.
Definition Arb8.cxx:439
virtual double ZHalfLength() const
Return the half-length along the z axis in mm.
Definition Arb8.cxx:466
virtual VGM::TwoVector Vertex(int index) const
Return the index-th vertex.
Definition Arb8.cxx:442
Arb8(const std::string &name, double hz, std::vector< VGM::TwoVector > vertices)
Definition Arb8.cxx:108
static double MaxTwistAngle(const std::vector< VGM::TwoVector > &vertices)
Definition Arb8.cxx:65
virtual double TwistAngle(int index) const
Return the index-th twist angle.
Definition Arb8.cxx:454
virtual std::string Name() const
Return the name of this solid.
Definition Arb8.cxx:433
static SolidMap * Instance()
Definition SolidMap.cxx:28
void AddSolid(VGM::ISolid *, G4VSolid *)
Definition SolidMap.cxx:59
#define M_PI
Definition of math constant M_PI not available on Win32.
Definition Math.h:24
BaseVGM utilities.
Definition utilities.h:23
ClhepVGM utilities.
Definition transform.h:29
Arb8SplitResult SplitArb8ForGenericTrap(double halfLength, const std::vector< VGM::TwoVector > &vertices, Arb8Split &split)
Split an Arb8 whose corresponding end edges form angles greater than 90 degrees into two pieces accep...
VGM interfaces.
Definition VMedium.h:28
std::pair< double, double > TwoVector
Definition TwoVector.h:28
Parameters of two G4GenericTrap-compatible pieces obtained by cutting an Arb8 with a plane perpendicu...
std::vector< VGM::TwoVector > upperVertices
std::vector< VGM::TwoVector > lowerVertices