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