Belle II Software  release-06-01-15
utilities.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 
9 #include <framework/logging/Logger.h>
10 #include <framework/gearbox/GearDir.h>
11 #include <geometry/utilities.h>
12 #include <geometry/GeometryManager.h>
13 
14 #include <boost/algorithm/string.hpp>
15 #include <boost/tuple/tuple.hpp>
16 
17 #include <G4Colour.hh>
18 #include <G4LogicalVolume.hh>
19 #include <G4VisAttributes.hh>
20 #include <G4Polycone.hh>
21 
22 #include <vector>
23 #include <limits>
24 #include <list>
25 
26 namespace Belle2 {
31  namespace geometry {
41  G4Colour parseColor(std::string colorString)
42  {
43  boost::to_lower(colorString);
44  double red(0), green(0), blue(0), alpha(0);
45  if (colorString[0] == '#') {
46  size_t size = colorString.size();
47  unsigned int colorValue;
48  std::stringstream in(colorString);
49  in.get();
50  in >> std::hex >> colorValue;
51  if (in.fail()) size = 0;
52  switch (size) {
53  case 4: //#rgb, add alpha since none was specified
54  colorValue = (colorValue << 4) + 15;
55  // and then continue with rgba case
56  [[fallthrough]];
57  case 5: //#rgba
58  red = ((colorValue & 0xf000) >> 12) / 15.;
59  green = ((colorValue & 0x0f00) >> 8) / 15.;
60  blue = ((colorValue & 0x00f0) >> 4) / 15.;
61  alpha = ((colorValue & 0x000f) >> 0) / 15.;
62  break;
63  case 7: //#rrggbb, add alpha since none was specified
64  colorValue = (colorValue << 8) + 255;
65  // and then continue with #rrggbbaa case
66  [[fallthrough]];
67  case 9: //#rrggbbaa
68  red = ((colorValue & 0xff000000) >> 24) / 255.;
69  green = ((colorValue & 0x00ff0000) >> 16) / 255.;
70  blue = ((colorValue & 0x0000ff00) >> 8) / 255.;
71  alpha = ((colorValue & 0x000000ff) >> 0) / 255.;
72  break;
73  default:
74  B2WARNING("Could not parse color string '" + colorString + "'");
75  }
76  } else if (colorString.substr(0, 3) == "rgb") {
77  //Parse value of the type rgb(1.0,1.0,1.0)
78  size_t startPos = colorString.find('(');
79  size_t stopPos = colorString.find(')');
80  std::string ws(" \t\r\n,");
81  std::stringstream in(colorString.substr(startPos + 1, stopPos - startPos - 1));
82  in >> red;
83  while (ws.find(in.peek()) != std::string::npos) in.get();
84  in >> green;
85  while (ws.find(in.peek()) != std::string::npos) in.get();
86  in >> blue;
87  while (ws.find(in.peek()) != std::string::npos) in.get();
88  in >> alpha;
89  red = std::min(1.0, std::max(0.0, red));
90  green = std::min(1.0, std::max(0.0, green));
91  blue = std::min(1.0, std::max(0.0, blue));
92  alpha = std::min(1.0, std::max(0.0, alpha));
93  }
94  return G4Colour(red, green, blue, alpha);
95  }
96 
97  void setColor(G4LogicalVolume& volume, const std::string& color)
98  {
99  auto* attr = const_cast<G4VisAttributes*>(volume.GetVisAttributes());
100  if (!attr) attr = GeometryManager::getInstance().newVisAttributes();
101  attr->SetColor(parseColor(color));
102  volume.SetVisAttributes(attr);
103  }
104 
105  void setVisibility(G4LogicalVolume& volume, bool visible)
106  {
107  auto* attr = const_cast<G4VisAttributes*>(volume.GetVisAttributes());
108  if (!attr) attr = GeometryManager::getInstance().newVisAttributes();
109  attr->SetVisibility(visible);
110  volume.SetVisAttributes(attr);
111  }
112 
113  G4Polycone* createPolyCone(const std::string& name, const GearDir& params, double& minZ, double& maxZ)
114  {
115  if (!params) return nullptr;
116 
117  double minPhi = params.getAngle("minPhi", 0);
118  double dPhi = params.getAngle("maxPhi", 2 * M_PI) - minPhi;
119  const std::vector<GearDir> planes = params.getNodes("Plane");
120  int nPlanes = planes.size();
121  if (nPlanes < 2) {
122  B2ERROR("Polycone needs at least two planes");
123  return nullptr;
124  }
125  std::vector<double> z(nPlanes, 0);
126  std::vector<double> rMin(nPlanes, 0);
127  std::vector<double> rMax(nPlanes, 0);
128  int index(0);
129  minZ = std::numeric_limits<double>::infinity();
130  maxZ = -std::numeric_limits<double>::infinity();
131  for (const GearDir& plane : planes) {
132  z[index] = plane.getLength("posZ") / Unit::mm;
133  minZ = std::min(minZ, z[index]);
134  maxZ = std::max(maxZ, z[index]);
135  rMin[index] = plane.getLength("innerRadius") / Unit::mm;
136  rMax[index] = plane.getLength("outerRadius") / Unit::mm;
137  ++index;
138  }
139  G4Polycone* polycone = new G4Polycone(name, minPhi, dPhi, nPlanes, z.data(), rMin.data(), rMax.data());
140  return polycone;
141  }
142 
143  //Use unnamed namespace to hide helper functions/definitions from outside
144  namespace {
146  using ZXPoint = std::pair<double, double>;
148  using PointList = std::list<ZXPoint>;
162  void subdivideSegments(const PointList& points, PointList& segments)
163  {
164  double lastZ = -std::numeric_limits<double>::infinity();
165  for (const ZXPoint& p : points) {
166  if (p.first < lastZ) {
167  B2FATAL("createRotationSolid: Points have to be given with ascending z positions");
168  }
169  lastZ = p.first;
170  //Now go over all segments. If the segments cross the points z
171  //coordinate we know that we need a new segment. We calculate the x
172  //position of the segment and insert a new point at that position in
173  //the list of points
174  auto segStart = segments.begin();
175  auto segEnd = segStart;
176  ++segEnd;
177  for (; segEnd != segments.end(); ++segEnd) {
178  if ((p.first > segStart->first && p.first < segEnd->first) ||
179  (p.first < segStart->first && p.first > segEnd->first)) {
180  double dZ = segEnd->first - segStart->first;
181  double dX = segEnd->second - segStart->second;
182  double newX = segStart->second + dX * (p.first - segStart->first) / dZ;
183  segments.insert(segEnd, ZXPoint(p.first, newX));
184  }
185  segStart = segEnd;
186  }
187  }
188 
189  //Now make sure the polyline extend over the same z range by adding the
190  //first/last point of points when neccessary
191  if (points.front().first < segments.front().first) {
192  segments.insert(segments.begin(), points.front());
193  }
194  if (points.back().first > segments.back().first) {
195  segments.insert(segments.end(), points.back());
196  }
197  }
198  }
199 
200  G4Polycone* createRotationSolid(const std::string& name, const GearDir& params, double& minZ, double& maxZ)
201  {
202 
203  //Make a list of all the points (ZX coordinates)
204  PointList innerPoints;
205  PointList outerPoints;
206  for (const GearDir& point : params.getNodes("InnerPoints/point")) {
207  innerPoints.push_back(ZXPoint(point.getLength("z") / Unit::mm, point.getLength("x") / Unit::mm));
208  }
209  for (const GearDir& point : params.getNodes("OuterPoints/point")) {
210  outerPoints.push_back(ZXPoint(point.getLength("z") / Unit::mm, point.getLength("x") / Unit::mm));
211  }
212  //Subdivide the segments to have an x position for each z specified for
213  //either inner or outer boundary
214  subdivideSegments(innerPoints, outerPoints);
215  subdivideSegments(outerPoints, innerPoints);
216  minZ = innerPoints.front().first;
217  maxZ = outerPoints.front().first;
218 
219  //Now we create the array of planes needed for the polycone
220  std::vector<double> z;
221  std::vector<double> rMin;
222  std::vector<double> rMax;
223 
224  double innerZ{0};
225  double innerX{0};
226  double outerZ{0};
227  double outerX{0};
228  //We go through both lists until both are empty
229  while (!(innerPoints.empty() && outerPoints.empty())) {
230  bool popInner = false;
231  //We could have more than one point at the same z position for segments
232  //going directly along x. because of that we check that the z
233  //coordinates for inner and outer line are always the same, reusing one
234  //point if neccessary
235  if (!innerPoints.empty() && innerPoints.front().first <= outerPoints.front().first) {
236  boost::tie(innerZ, innerX) = innerPoints.front();
237  popInner = true;
238  }
239  if (!outerPoints.empty() && outerPoints.front().first <= innerPoints.front().first) {
240  boost::tie(outerZ, outerX) = outerPoints.front();
241  outerPoints.pop_front();
242  }
243  if (popInner) innerPoints.pop_front();
244  if (innerZ != outerZ) {
245  B2ERROR("createRotationSolid: Something is wrong, z values should be identical");
246  return nullptr;
247  }
248  z.push_back(innerZ);
249  rMin.push_back(innerX);
250  rMax.push_back(outerX);
251  }
252 
253  //Finally create the Polycone
254  int nPlanes = z.size();
255  double minPhi = params.getAngle("minPhi", 0);
256  double dPhi = params.getAngle("maxPhi", 2 * M_PI) - minPhi;
257  return new G4Polycone(name, minPhi, dPhi, nPlanes, &z.front(), &rMin.front(), &rMax.front());
258  }
259 
270  G4Polycone* createRotationSolid(const std::string& name,
271  std::list< std::pair<double, double> > innerPoints,
272  std::list< std::pair<double, double> > outerPoints,
273  double minPhi, double maxPhi, double& minZ, double& maxZ)
274  {
275  //Subdivide the segments to have an x position for each z specified for
276  //either inner or outer boundary
277  subdivideSegments(innerPoints, outerPoints);
278  subdivideSegments(outerPoints, innerPoints);
279  minZ = innerPoints.front().first;
280  maxZ = outerPoints.front().first;
281 
282  //Now we create the array of planes needed for the polycone
283  std::vector<double> z;
284  std::vector<double> rMin;
285  std::vector<double> rMax;
286 
287  double innerZ{0};
288  double innerX{0};
289  double outerZ{0};
290  double outerX{0};
291  //We go through both lists until both are empty
292  while (!(innerPoints.empty() && outerPoints.empty())) {
293  bool popInner = false;
294  //We could have more than one point at the same z position for segments
295  //going directly along x. because of that we check that the z
296  //coordinates for inner and outer line are always the same, reusing one
297  //point if neccessary
298  if (!innerPoints.empty() && innerPoints.front().first <= outerPoints.front().first) {
299  boost::tie(innerZ, innerX) = innerPoints.front();
300  popInner = true;
301  }
302  if (!outerPoints.empty() && outerPoints.front().first <= innerPoints.front().first) {
303  boost::tie(outerZ, outerX) = outerPoints.front();
304  outerPoints.pop_front();
305  }
306  if (popInner) innerPoints.pop_front();
307  if (innerZ != outerZ) {
308  B2ERROR("createRotationSolid: Something is wrong, z values should be identical");
309  return nullptr;
310  }
311  z.push_back(innerZ / Unit::mm);
312  rMin.push_back(innerX / Unit::mm);
313  rMax.push_back(outerX / Unit::mm);
314  }
315 
316  //Finally create the Polycone
317  int nPlanes = z.size();
318  double dPhi = maxPhi - minPhi;
319  return new G4Polycone(name, minPhi, dPhi, nPlanes, &z.front(), &rMin.front(), &rMax.front());
320 
321  }
322 
323 
324  }
326 } //Belle2 namespace
GearDir is the basic class used for accessing the parameter store.
Definition: GearDir.h:31
static const double mm
[millimeters]
Definition: Unit.h:70
G4VisAttributes * newVisAttributes()
Create an anonymous G4VisAttributes for an existing G4LogicalVolume.
static GeometryManager & getInstance()
Return a reference to the instance.
G4Polycone * createPolyCone(const std::string &name, const GearDir &params, double &minZ, double &maxZ)
Create Polycone Shape from XML Parameters.
Definition: utilities.cc:113
G4Polycone * createRotationSolid(const std::string &name, const GearDir &params, double &minZ, double &maxZ)
Create a solid by roating two polylines around the Z-Axis.
Definition: utilities.cc:200
void setVisibility(G4LogicalVolume &volume, bool visible)
Helper function to quickly set the visibility of a given volume.
Definition: utilities.cc:105
void setColor(G4LogicalVolume &volume, const std::string &color)
Set the color of a logical volume.
Definition: utilities.cc:97
G4Colour parseColor(std::string colorString)
Parse a color string of the form #rgb, #rrggbb, #rgba, #rrggbbaa or rgb(r, g, b) and return a corresp...
Definition: utilities.cc:41
Abstract base class for different kinds of events.