Project
Loading...
Searching...
No Matches
O2SurfaceSolidIO.cxx
Go to the documentation of this file.
1// Copyright 2019-2026 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
13
16
21
22#include "BoundedSurface.h"
23
24#include <TError.h>
25
26#include <cmath>
27#include <cstdint>
28#include <cstring>
29#include <fstream>
30#include <vector>
31
32namespace o2
33{
34namespace cad
35{
36
38
39namespace
40{
41
44constexpr uint32_t kSidecarVersionMin = 1;
45constexpr uint32_t kSidecarVersionMax = 3;
46
48constexpr double kSidecarV1FallbackTolerance = 1.e-6;
49
50constexpr uint32_t kFlagInnerWall = 1u << 0;
51
53constexpr uint32_t kFlatCSGVersion = 1;
54constexpr uint64_t kFlatCSGHalfspaceBytes = 100;
55constexpr uint64_t kFlatCSGCellBytes = 64;
56
57enum SurfaceType : uint32_t {
58 kPlane = 1,
59 kCylinder = 2,
60 kCone = 3,
61 kSphere = 4,
62 kTorus = 5,
63};
64
65enum CurveType : uint32_t {
66 kLineSegment = 0,
67 kCircularArc = 1,
68 kBSpline2D = 2,
69};
70
72bool parseBSplineEdge(const std::vector<double>& params, O2BVHSurfaceSolid::PlanarBoundaryCurve& curve)
73{
74 if (params.size() < 2) {
75 return false;
76 }
77 const int degree = static_cast<int>(std::lround(params[0]));
78 const int nPoles = static_cast<int>(std::lround(params[1]));
79 if (degree < 1 || nPoles < degree + 1) {
80 return false;
81 }
82 const size_t nKnots = static_cast<size_t>(nPoles) + degree + 1;
83 const size_t expected = 2 + 2 * static_cast<size_t>(nPoles) + static_cast<size_t>(nPoles) + nKnots;
84 if (params.size() < expected) {
85 return false;
86 }
87 std::vector<O2BVHSurfaceSolid::Point2D> poles(nPoles);
88 size_t offset = 2;
89 for (int i = 0; i < nPoles; ++i) {
90 poles[i] = {params[offset], params[offset + 1]};
91 offset += 2;
92 }
93 std::vector<double> weights(nPoles);
94 for (int i = 0; i < nPoles; ++i) {
95 weights[i] = params[offset++];
96 }
97 std::vector<double> knots(nKnots);
98 for (size_t i = 0; i < nKnots; ++i) {
99 knots[i] = params[offset++];
100 }
101 curve = O2BVHSurfaceSolid::PlanarBoundaryCurve::makeBSpline(degree, std::move(poles), std::move(weights),
102 std::move(knots));
103 return true;
104}
105
106struct SidecarEdge {
108 std::vector<double> params;
109};
110
111struct SidecarWire {
112 uint32_t role = 0; // 0 = outer, 1 = inner
113 std::vector<SidecarEdge> edges;
114};
115
117template <typename T>
118bool readValue(std::ifstream& in, T& value)
119{
120 in.read(reinterpret_cast<char*>(&value), sizeof(value));
121 return static_cast<bool>(in);
122}
123
125template <typename T>
126void writeValue(std::ofstream& out, const T& value)
127{
128 out.write(reinterpret_cast<const char*>(&value), sizeof(value));
129}
130
132uint64_t bytesRemaining(std::ifstream& in, std::streamoff fileSize)
133{
134 if (!in) {
135 return 0;
136 }
137 const std::streamoff here = in.tellg();
138 return here < 0 || here > fileSize ? 0 : static_cast<uint64_t>(fileSize - here);
139}
140
141bool readDoubles(std::ifstream& in, std::vector<double>& values, uint32_t n, std::streamoff fileSize)
142{
143 if (static_cast<uint64_t>(n) * sizeof(double) > bytesRemaining(in, fileSize)) {
144 return false;
145 }
146 values.resize(n);
147 in.read(reinterpret_cast<char*>(values.data()), static_cast<std::streamsize>(n) * sizeof(double));
148 return static_cast<bool>(in);
149}
150
151O2BVHSurfaceSolid::Point3D point3(const std::vector<double>& p, size_t offset)
152{
153 return {p[offset], p[offset + 1], p[offset + 2]};
154}
155
157bool edgeEndpoints(const SidecarEdge& edge, O2BVHSurfaceSolid::Point2D& start, O2BVHSurfaceSolid::Point2D& end,
158 O2BVHSurfaceSolid::PlanarBoundaryCurve& bspline)
159{
160 if (edge.curveType == kLineSegment && edge.params.size() >= 4) {
161 start = {edge.params[0], edge.params[1]};
162 end = {edge.params[2], edge.params[3]};
163 return true;
164 }
165 if (edge.curveType == kCircularArc && edge.params.size() >= 5) {
166 const double cu = edge.params[0], cv = edge.params[1], r = edge.params[2];
167 const double a0 = edge.params[3], a1 = edge.params[3] + edge.params[4];
168 start = {cu + r * std::cos(a0), cv + r * std::sin(a0)};
169 end = {cu + r * std::cos(a1), cv + r * std::sin(a1)};
170 return true;
171 }
172 if (edge.curveType == kBSpline2D) {
173 if (!parseBSplineEdge(edge.params, bspline)) {
174 return false;
175 }
176 // Evaluate the curve rather than read its first and last poles, which lie off the curve for an
177 // unclamped or periodic knot vector.
178 std::vector<surface::Vec2> poles;
179 poles.reserve(bspline.poles.size());
180 for (const auto& pole : bspline.poles) {
181 poles.push_back({pole[0], pole[1]});
182 }
183 const surface::Curve2D evaluated =
184 surface::Curve2D::makeBSpline(bspline.degree, std::move(poles), bspline.weights, bspline.knots);
185 const surface::Vec2 first = evaluated.startPoint();
186 const surface::Vec2 last = evaluated.endPoint();
187 start = {first.uCoord, first.vCoord};
188 end = {last.uCoord, last.vCoord};
189 return true;
190 }
191 return false;
192}
193
195struct RecordMetric {
197 const double* params = nullptr;
198
199 static void evaluate(const void* context, const surface::Vec2& uv, double& gUU, double& gUV, double& gVV)
200 {
201 const auto& record = *static_cast<const RecordMetric*>(context);
202 const double* p = record.params;
203 switch (record.surfaceType) {
204 case kPlane:
205 surface::planeParametricMetric({p[3], p[4], p[5]}, {p[6], p[7], p[8]}, gUU, gUV, gVV);
206 return;
207 case kCylinder:
208 surface::cylinderParametricMetric(p[9], gUU, gUV, gVV);
209 return;
210 case kCone: {
211 // r(h) = radiusAtMin + slope * (h - heightMin), with slope from the two radii/heights
212 const double slope = (p[10] - p[9]) / (p[12] - p[11]);
213 surface::coneParametricMetric(p[9] + slope * (uv.vCoord - p[11]), slope, gUU, gUV, gVV);
214 return;
215 }
216 case kSphere:
217 surface::sphereParametricMetric(p[9], uv.vCoord, gUU, gUV, gVV);
218 return;
219 case kTorus:
220 surface::torusParametricMetric(p[9], p[10], uv.vCoord, gUU, gUV, gVV);
221 return;
222 default:
223 // an unknown type is rejected further down; the identity keeps this total meanwhile
224 gUU = 1.;
225 gUV = 0.;
226 gVV = 1.;
227 return;
228 }
229 }
230
231 surface::ParametricMetric metric() const { return {&evaluate, this}; }
232};
233
236bool wireToCurves(const std::string& file, size_t surfaceIndex, const SidecarWire& wire,
237 std::vector<O2BVHSurfaceSolid::PlanarBoundaryCurve>& curves, bool& anyArc,
238 const surface::ParametricMetric& metric, double joinTolerance, const char* toleranceOrigin)
239{
240 using Curve = O2BVHSurfaceSolid::PlanarBoundaryCurve;
241 curves.clear();
242 curves.reserve(wire.edges.size());
243 // every edge's endpoints, and a B-spline edge's parsed curve, computed once
244 const size_t nEdges = wire.edges.size();
245 std::vector<O2BVHSurfaceSolid::Point2D> starts(nEdges);
246 std::vector<O2BVHSurfaceSolid::Point2D> ends(nEdges);
247 std::vector<Curve> bsplines(nEdges);
248 for (size_t e = 0; e < nEdges; ++e) {
249 if (!edgeEndpoints(wire.edges[e], starts[e], ends[e], bsplines[e])) {
250 ::Error("LoadSurfaceSolid", "%s: surface %zu: unsupported or malformed wire edge %zu", file.c_str(),
251 surfaceIndex, e);
252 return false;
253 }
254 }
255 for (size_t e = 0; e < nEdges; ++e) {
256 const auto& edge = wire.edges[e];
257 const auto& end = ends[e];
258 const auto& nextStart = starts[(e + 1) % nEdges];
259 const double joinGapSq = metric.distanceSq({end[0], end[1]}, {nextStart[0], nextStart[1]});
260 if (joinGapSq > joinTolerance * joinTolerance) {
261 ::Error("LoadSurfaceSolid",
262 "%s: surface %zu: wire edge %zu end does not join the next edge start (gap %.3g cm, tolerance %.3g cm, "
263 "%s)",
264 file.c_str(), surfaceIndex, e, std::sqrt(joinGapSq), joinTolerance, toleranceOrigin);
265 return false;
266 }
267 if (edge.curveType == kCircularArc) {
268 anyArc = true;
269 curves.push_back(Curve::makeArc({edge.params[0], edge.params[1]}, edge.params[2], edge.params[3],
270 edge.params[3] + edge.params[4]));
271 } else if (edge.curveType == kBSpline2D) {
272 anyArc = true; // a bspline is a curved edge, so route the plane through AddCurvedPlanarSurface
273 curves.push_back(std::move(bsplines[e]));
274 } else {
275 curves.push_back(Curve::makeLine(starts[e], end));
276 }
277 }
278 return true;
279}
280
282struct TrimWording {
283 const char* moreThanOneOuter;
284 const char* noOuter;
285};
286constexpr TrimWording kPlaneWording{"%s: plane surface %zu has more than one outer wire",
287 "%s: plane surface %zu has no outer wire"};
288constexpr TrimWording kQuadricWording{"%s: quadric surface %zu has more than one outer trim wire",
289 "%s: quadric surface %zu trim block has no outer wire"};
290
292bool collectTrim(const std::string& file, size_t surfaceIndex, const TrimWording& wording,
293 const std::vector<SidecarWire>& wires, std::vector<O2BVHSurfaceSolid::PlanarBoundaryCurve>& outer,
294 std::vector<std::vector<O2BVHSurfaceSolid::PlanarBoundaryCurve>>& inners, bool& anyArc,
295 const surface::ParametricMetric& metric, double joinTolerance, const char* toleranceOrigin)
296{
297 bool haveOuter = false;
298 for (const auto& wire : wires) {
299 std::vector<O2BVHSurfaceSolid::PlanarBoundaryCurve> curves;
300 if (!wireToCurves(file, surfaceIndex, wire, curves, anyArc, metric, joinTolerance, toleranceOrigin)) {
301 return false;
302 }
303 if (wire.role == 0) {
304 if (haveOuter) {
305 ::Error("LoadSurfaceSolid", wording.moreThanOneOuter, file.c_str(), surfaceIndex);
306 return false;
307 }
308 outer = std::move(curves);
309 haveOuter = true;
310 } else {
311 inners.push_back(std::move(curves));
312 }
313 }
314 if (!haveOuter) {
315 ::Error("LoadSurfaceSolid", wording.noOuter, file.c_str(), surfaceIndex);
316 return false;
317 }
318 return true;
319}
320
322void reorderEdgeRefsToKernelOrder(const std::vector<SidecarWire>& wires, std::vector<unsigned int>& edgeIds,
323 std::vector<unsigned char>& edgeFlags)
324{
325 size_t totalEdges = 0;
326 for (const auto& wire : wires) {
327 totalEdges += wire.edges.size();
328 }
329 if (wires.empty() || totalEdges != edgeIds.size()) {
330 return;
331 }
332 // kernel offset of each sidecar wire: the outer wire first, then the inner wires in file order
333 std::vector<size_t> kernelOffset(wires.size(), 0);
334 size_t running = 0;
335 for (size_t w = 0; w < wires.size(); ++w) {
336 if (wires[w].role == 0) {
337 kernelOffset[w] = 0;
338 running = wires[w].edges.size();
339 break;
340 }
341 }
342 for (size_t w = 0; w < wires.size(); ++w) {
343 if (wires[w].role != 0) {
344 kernelOffset[w] = running;
345 running += wires[w].edges.size();
346 }
347 }
348
349 std::vector<unsigned int> permutedIds(edgeIds.size());
350 std::vector<unsigned char> permutedFlags(edgeFlags.size());
351 size_t sidecarOffset = 0;
352 for (size_t w = 0; w < wires.size(); ++w) {
353 for (size_t e = 0; e < wires[w].edges.size(); ++e) {
354 permutedIds[kernelOffset[w] + e] = edgeIds[sidecarOffset + e];
355 permutedFlags[kernelOffset[w] + e] = edgeFlags[sidecarOffset + e];
356 }
357 sidecarOffset += wires[w].edges.size();
358 }
359 edgeIds.swap(permutedIds);
360 edgeFlags.swap(permutedFlags);
361}
362
363} // namespace
364
365bool LoadSurfaceSolid(const std::string& file, O2BVHSurfaceSolid& solid)
366{
367 std::ifstream in(file, std::ios::binary);
368 if (!in) {
369 ::Error("LoadSurfaceSolid", "Cannot open surface sidecar file %s", file.c_str());
370 return false;
371 }
372
373 in.seekg(0, std::ios::end);
374 const std::streamoff fileSize = in.tellg();
375 in.seekg(0, std::ios::beg);
376
377 char magic[4];
378 in.read(magic, sizeof(magic));
379 if (!in || std::memcmp(magic, "O2SS", 4) != 0) {
380 ::Error("LoadSurfaceSolid", "%s is not a surface sidecar file (bad magic)", file.c_str());
381 return false;
382 }
383
384 uint32_t version = 0, nSurfaces = 0, reserved = 0;
385 if (!readValue(in, version) || !readValue(in, nSurfaces) || !readValue(in, reserved)) {
386 ::Error("LoadSurfaceSolid", "%s: truncated header", file.c_str());
387 return false;
388 }
389 if (version < kSidecarVersionMin || version > kSidecarVersionMax) {
390 ::Error("LoadSurfaceSolid", "%s: unsupported sidecar version %u (reader supports %u..%u)", file.c_str(), version,
391 kSidecarVersionMin, kSidecarVersionMax);
392 return false;
393 }
394
395 uint32_t nModelEdges = 0;
396 if (version >= 2) {
397 double modelTolerance = 0.;
398 if (!readValue(in, modelTolerance)) {
399 ::Error("LoadSurfaceSolid", "%s: truncated version-2 header (no model tolerance)", file.c_str());
400 return false;
401 }
402 solid.SetModelTolerance(modelTolerance);
403 if (version >= 3 && !readValue(in, nModelEdges)) {
404 ::Error("LoadSurfaceSolid", "%s: truncated version-3 header (no edge table size)", file.c_str());
405 return false;
406 }
407 } else {
408 ::Warning("LoadSurfaceSolid",
409 "%s is a version-1 sidecar and states no model tolerance; assuming %g cm (the extractor's precision). "
410 "Re-run the converter to record the model's own value.",
411 file.c_str(), kSidecarV1FallbackTolerance);
412 solid.SetModelTolerance(kSidecarV1FallbackTolerance);
413 }
414
415 // the wire-join band, from the header: the band the kernel's Add*Surface applies to the same wires
416 const double joinTolerance = surface::wireJoinToleranceFor(solid.GetModelTolerance());
417 const char* toleranceOrigin = joinTolerance > surface::kWireJoinTolerance
418 ? "declared by the model"
419 : "the extractor-precision fallback";
420
421 for (size_t s = 0; s < nSurfaces; ++s) {
422 uint32_t surfaceType = 0, flags = 0, nParams = 0;
423 if (!readValue(in, surfaceType) || !readValue(in, flags) || !readValue(in, nParams)) {
424 ::Error("LoadSurfaceSolid", "%s: truncated surface record %zu", file.c_str(), s);
425 return false;
426 }
427 std::vector<double> p;
428 if (!readDoubles(in, p, nParams, fileSize)) {
429 ::Error("LoadSurfaceSolid", "%s: truncated parameters of surface %zu", file.c_str(), s);
430 return false;
431 }
432
433 // The wire block is self-describing; read it unconditionally.
434 uint32_t nWires = 0;
435 if (!readValue(in, nWires)) {
436 ::Error("LoadSurfaceSolid", "%s: truncated wire count of surface %zu", file.c_str(), s);
437 return false;
438 }
439 // 8 bytes of header per wire is the floor, so a count beyond that cannot be honest
440 if (static_cast<uint64_t>(nWires) * 8u > bytesRemaining(in, fileSize)) {
441 ::Error("LoadSurfaceSolid", "%s: surface %zu claims %u wires, more than the file holds", file.c_str(), s, nWires);
442 return false;
443 }
444 std::vector<SidecarWire> wires(nWires);
445 for (auto& wire : wires) {
446 uint32_t nEdges = 0;
447 if (!readValue(in, wire.role) || !readValue(in, nEdges)) {
448 ::Error("LoadSurfaceSolid", "%s: truncated wire header in surface %zu", file.c_str(), s);
449 return false;
450 }
451 if (static_cast<uint64_t>(nEdges) * 8u > bytesRemaining(in, fileSize)) {
452 ::Error("LoadSurfaceSolid", "%s: surface %zu claims %u wire edges, more than the file holds", file.c_str(), s,
453 nEdges);
454 return false;
455 }
456 wire.edges.resize(nEdges);
457 for (auto& edge : wire.edges) {
458 uint32_t nCurveParams = 0;
459 if (!readValue(in, edge.curveType) || !readValue(in, nCurveParams) ||
460 !readDoubles(in, edge.params, nCurveParams, fileSize)) {
461 ::Error("LoadSurfaceSolid", "%s: truncated edge record in surface %zu", file.c_str(), s);
462 return false;
463 }
464 }
465 }
466
467 // Version 3: the face's boundary edge identities, in the sidecar's own wire order.
468 std::vector<unsigned int> edgeIds;
469 std::vector<unsigned char> edgeFlags;
470 if (version >= 3) {
471 uint32_t nEdgeRefs = 0;
472 if (!readValue(in, nEdgeRefs)) {
473 ::Error("LoadSurfaceSolid", "%s: truncated edge identity count of surface %zu", file.c_str(), s);
474 return false;
475 }
476 if (static_cast<uint64_t>(nEdgeRefs) * 5u > bytesRemaining(in, fileSize)) {
477 ::Error("LoadSurfaceSolid", "%s: surface %zu claims %u edge identities, more than the file holds",
478 file.c_str(), s, nEdgeRefs);
479 return false;
480 }
481 edgeIds.resize(nEdgeRefs);
482 edgeFlags.resize(nEdgeRefs);
483 for (uint32_t e = 0; e < nEdgeRefs; ++e) {
484 uint32_t edgeId = 0;
485 uint8_t edgeFlag = 0;
486 if (!readValue(in, edgeId) || !readValue(in, edgeFlag)) {
487 ::Error("LoadSurfaceSolid", "%s: truncated edge identity %u of surface %zu", file.c_str(), e, s);
488 return false;
489 }
490 if (nModelEdges > 0 && edgeId >= nModelEdges) {
491 ::Error("LoadSurfaceSolid", "%s: surface %zu edge identity %u is %u, outside the model's %u edge(s)",
492 file.c_str(), s, e, edgeId, nModelEdges);
493 return false;
494 }
495 edgeIds[e] = edgeId;
496 edgeFlags[e] = edgeFlag;
497 }
498 }
499
500 const bool innerWall = (flags & kFlagInnerWall) != 0;
501 const RecordMetric recordMetric{surfaceType, p.data()};
502 bool added = false;
503
504 // one quadric: check the parameter count, then add the surface untrimmed or with its trim block
505 const auto addQuadric = [&](const char* name, uint32_t expectedParams, const auto& addUntrimmed,
506 const auto& addTrimmed) {
507 if (nParams != expectedParams) {
508 ::Error("LoadSurfaceSolid", "%s: %s surface %zu has %u parameters, expected %u", file.c_str(), name, s,
509 nParams, expectedParams);
510 return false;
511 }
512 if (wires.empty()) {
513 added = addUntrimmed();
514 return true;
515 }
516 std::vector<O2BVHSurfaceSolid::PlanarBoundaryCurve> outer;
517 std::vector<std::vector<O2BVHSurfaceSolid::PlanarBoundaryCurve>> inners;
518 bool anyArc = false; // quadric domains accept both line and arc trim edges
519 if (!collectTrim(file, s, kQuadricWording, wires, outer, inners, anyArc, recordMetric.metric(), joinTolerance,
520 toleranceOrigin)) {
521 return false;
522 }
523 added = addTrimmed(outer, inners);
524 return true;
525 };
526
527 switch (surfaceType) {
528 case kPlane: {
529 if (nParams != 9) {
530 ::Error("LoadSurfaceSolid", "%s: plane surface %zu has %u parameters, expected 9", file.c_str(), s, nParams);
531 return false;
532 }
533 // Read every wire as a general line/arc loop. A pure line-segment loop keeps the
534 // polygon path (AddPlanarSurface, general-metric); any arc routes to the curved path.
535 std::vector<O2BVHSurfaceSolid::PlanarBoundaryCurve> outer;
536 std::vector<std::vector<O2BVHSurfaceSolid::PlanarBoundaryCurve>> inners;
537 bool anyArc = false;
538 if (!collectTrim(file, s, kPlaneWording, wires, outer, inners, anyArc, recordMetric.metric(), joinTolerance,
539 toleranceOrigin)) {
540 return false;
541 }
542 if (anyArc) {
543 added = solid.AddCurvedPlanarSurface(point3(p, 0), point3(p, 3), point3(p, 6), outer, inners);
544 } else {
545 const auto toPolygon = [](const std::vector<O2BVHSurfaceSolid::PlanarBoundaryCurve>& curves) {
546 std::vector<O2BVHSurfaceSolid::Point2D> polygon;
547 polygon.reserve(curves.size());
548 for (const auto& c : curves) {
549 polygon.push_back(c.lineStart);
550 }
551 return polygon;
552 };
553 std::vector<std::vector<O2BVHSurfaceSolid::Point2D>> innerPolys;
554 innerPolys.reserve(inners.size());
555 for (const auto& inner : inners) {
556 innerPolys.push_back(toPolygon(inner));
557 }
558 added = solid.AddPlanarSurface(point3(p, 0), point3(p, 3), point3(p, 6), toPolygon(outer), innerPolys);
559 }
560 break;
561 }
562 case kCylinder:
563 if (!addQuadric(
564 "cylinder", 14,
565 [&] {
566 return solid.AddCylindricalSurface(point3(p, 0), point3(p, 3), point3(p, 6), p[9], p[10], p[11], p[12],
567 p[13], innerWall);
568 },
569 [&](const auto& outer, const auto& inners) {
570 return solid.AddCylindricalSurface(point3(p, 0), point3(p, 3), point3(p, 6), p[9], p[10], p[11], p[12],
571 p[13], innerWall, outer, inners);
572 })) {
573 return false;
574 }
575 break;
576 case kCone:
577 if (!addQuadric(
578 "cone", 15,
579 [&] {
580 return solid.AddConicalSurface(point3(p, 0), point3(p, 3), point3(p, 6), p[9], p[10], p[11], p[12],
581 p[13], p[14], innerWall);
582 },
583 [&](const auto& outer, const auto& inners) {
584 return solid.AddConicalSurface(point3(p, 0), point3(p, 3), point3(p, 6), p[9], p[10], p[11], p[12],
585 p[13], p[14], innerWall, outer, inners);
586 })) {
587 return false;
588 }
589 break;
590 case kSphere:
591 if (!addQuadric(
592 "sphere", 14,
593 [&] {
594 return solid.AddSphericalSurface(point3(p, 0), point3(p, 3), point3(p, 6), p[9], p[10], p[11], p[12],
595 p[13], innerWall);
596 },
597 [&](const auto& outer, const auto& inners) {
598 return solid.AddSphericalSurface(point3(p, 0), point3(p, 3), point3(p, 6), p[9], p[10], p[11], p[12],
599 p[13], innerWall, outer, inners);
600 })) {
601 return false;
602 }
603 break;
604 case kTorus:
605 if (!addQuadric(
606 "torus", 15,
607 [&] {
608 return solid.AddToroidalSurface(point3(p, 0), point3(p, 3), point3(p, 6), p[9], p[10], p[11], p[12],
609 p[13], p[14], innerWall);
610 },
611 [&](const auto& outer, const auto& inners) {
612 return solid.AddToroidalSurface(point3(p, 0), point3(p, 3), point3(p, 6), p[9], p[10], p[11], p[12],
613 p[13], p[14], innerWall, outer, inners);
614 })) {
615 return false;
616 }
617 break;
618 default:
619 ::Error("LoadSurfaceSolid", "%s: surface %zu has unknown surface type %u", file.c_str(), s, surfaceType);
620 return false;
621 }
622
623 if (!added) {
624 ::Error("LoadSurfaceSolid", "%s: surface %zu was rejected by O2BVHSurfaceSolid", file.c_str(), s);
625 return false;
626 }
627 if (!edgeIds.empty()) {
628 reorderEdgeRefsToKernelOrder(wires, edgeIds, edgeFlags);
629 solid.SetSurfaceBoundaryEdges(static_cast<int>(s), edgeIds, edgeFlags);
630 }
631 }
632
633 return true;
634}
635
636bool LoadFacetSolid(const std::string& file, O2Tessellated& solid)
637{
638 std::ifstream in(file, std::ios::binary);
639 if (!in) {
640 ::Error("LoadFacetSolid", "Cannot open facet sidecar file %s", file.c_str());
641 return false;
642 }
643
644 in.seekg(0, std::ios::end);
645 const std::streamoff fileSize = in.tellg();
646 in.seekg(0, std::ios::beg);
647
648 uint32_t nTriangles = 0;
649 if (!readValue(in, nTriangles)) {
650 ::Error("LoadFacetSolid", "%s: truncated header", file.c_str());
651 return false;
652 }
653
654 // one record is nine float32; the count is checked against the file before one block read
655 const uint64_t recordsBytes = static_cast<uint64_t>(nTriangles) * 9u * sizeof(float);
656 const uint64_t remaining = bytesRemaining(in, fileSize);
657 if (recordsBytes > remaining) {
658 ::Error("LoadFacetSolid", "%s: truncated: %u facet record(s) need %llu byte(s), found %llu", file.c_str(),
659 nTriangles, static_cast<unsigned long long>(recordsBytes), static_cast<unsigned long long>(remaining));
660 return false;
661 }
662 std::vector<float> records(9 * static_cast<size_t>(nTriangles));
663 in.read(reinterpret_cast<char*>(records.data()), static_cast<std::streamsize>(recordsBytes));
664 if (!in) {
665 ::Error("LoadFacetSolid", "%s: truncated facet records", file.c_str());
666 return false;
667 }
668
669 uint32_t nDegenerate = 0;
670 for (uint32_t i = 0; i < nTriangles; ++i) {
671 const float* v = &records[9 * static_cast<size_t>(i)];
672 const O2Tessellated::Vertex_t p0(v[0], v[1], v[2]);
673 const O2Tessellated::Vertex_t p1(v[3], v[4], v[5]);
674 const O2Tessellated::Vertex_t p2(v[6], v[7], v[8]);
675 if (!solid.AddFacet(p0, p1, p2)) {
676 // a degenerate facet is a mesh property, not a format error: count it and carry on
677 ++nDegenerate;
678 continue;
679 }
680 }
681 if (nDegenerate > 0) {
682 ::Warning("LoadFacetSolid", "%s: skipped %u degenerate facet(s) of %u", file.c_str(), nDegenerate, nTriangles);
683 }
684
685 return true;
686}
687
688bool LoadFlatCSG(const std::string& file, O2FlatCSG& solid)
689{
690 std::ifstream in(file, std::ios::binary);
691 if (!in) {
692 ::Error("LoadFlatCSG", "Cannot open flat-CSG sidecar file %s", file.c_str());
693 return false;
694 }
695
696 in.seekg(0, std::ios::end);
697 const std::streamoff fileSize = in.tellg();
698 in.seekg(0, std::ios::beg);
699
700 char magic[8];
701 in.read(magic, sizeof(magic));
702 if (!in || std::memcmp(magic, "O2FLTCSG", sizeof(magic)) != 0) {
703 ::Error("LoadFlatCSG", "%s is not a flat-CSG sidecar file (bad magic)", file.c_str());
704 return false;
705 }
706
707 uint32_t version = 0, nHalfspaces = 0, nCells = 0;
708 if (!readValue(in, version) || !readValue(in, nHalfspaces) || !readValue(in, nCells)) {
709 ::Error("LoadFlatCSG", "%s: truncated header", file.c_str());
710 return false;
711 }
712 if (version != kFlatCSGVersion) {
713 ::Error("LoadFlatCSG", "%s: unsupported sidecar version %u (reader supports %u)", file.c_str(), version,
714 kFlatCSGVersion);
715 return false;
716 }
717
718 // refuse a file whose length does not match its header before reading a record
719 const uint64_t expected =
720 static_cast<uint64_t>(nHalfspaces) * kFlatCSGHalfspaceBytes + static_cast<uint64_t>(nCells) * kFlatCSGCellBytes;
721 const uint64_t remaining = bytesRemaining(in, fileSize);
722 if (remaining != expected) {
723 ::Error("LoadFlatCSG",
724 "%s: file length does not match its header (%u halfspace(s) + %u cell(s) implies %llu more byte(s), "
725 "found %llu)",
726 file.c_str(), nHalfspaces, nCells, static_cast<unsigned long long>(expected),
727 static_cast<unsigned long long>(remaining));
728 return false;
729 }
730
731 // Every field below is read on its own -- see readValue's comment on why a struct-based read of
732 // the 100-byte halfspace record would be wrong for every record after the first.
733 for (uint32_t h = 0; h < nHalfspaces; ++h) {
734 int32_t kind = 0;
735 double sign = 0.;
736 if (!readValue(in, kind) || !readValue(in, sign)) {
737 ::Error("LoadFlatCSG", "%s: truncated halfspace record %u", file.c_str(), h);
738 return false;
739 }
740 double c[11];
741 bool ok = true;
742 for (int i = 0; i < 11 && ok; ++i) {
743 ok = readValue(in, c[i]);
744 }
745 if (!ok) {
746 ::Error("LoadFlatCSG", "%s: truncated halfspace record %u", file.c_str(), h);
747 return false;
748 }
749 bool finite = std::isfinite(sign);
750 for (double value : c) {
751 finite = finite && std::isfinite(value);
752 }
753 if (!finite) {
754 ::Error("LoadFlatCSG", "%s: halfspace %u has a non-finite coefficient", file.c_str(), h);
755 return false;
756 }
758 solid.AddQuadric(sign, c);
759 } else if (kind == FlatCSGHalfspace::kTorus) {
760 const double centre[3] = {c[0], c[1], c[2]};
761 const double axis[3] = {c[3], c[4], c[5]};
762 if (!(axis[0] * axis[0] + axis[1] * axis[1] + axis[2] * axis[2] > 0.)) {
763 ::Error("LoadFlatCSG", "%s: torus halfspace %u has a zero axis", file.c_str(), h);
764 return false;
765 }
766 solid.AddTorus(sign, centre, axis, c[6], c[7]);
767 } else {
768 ::Error("LoadFlatCSG", "%s: halfspace %u has unknown kind %d", file.c_str(), h, kind);
769 return false;
770 }
771 }
772
773 for (uint32_t cellIdx = 0; cellIdx < nCells; ++cellIdx) {
774 int32_t first = 0, count = 0;
775 double volume = 0.;
776 if (!readValue(in, first) || !readValue(in, count) || !readValue(in, volume)) {
777 ::Error("LoadFlatCSG", "%s: truncated cell record %u", file.c_str(), cellIdx);
778 return false;
779 }
780 double lo[3], hi[3];
781 bool ok = true;
782 for (int i = 0; i < 3 && ok; ++i) {
783 ok = readValue(in, lo[i]);
784 }
785 for (int i = 0; i < 3 && ok; ++i) {
786 ok = readValue(in, hi[i]);
787 }
788 if (!ok) {
789 ::Error("LoadFlatCSG", "%s: truncated cell record %u", file.c_str(), cellIdx);
790 return false;
791 }
792 if (first < 0 || count <= 0 || static_cast<int64_t>(first) + count > static_cast<int64_t>(nHalfspaces)) {
793 ::Error("LoadFlatCSG", "%s: cell %u has an invalid range (first=%d, count=%d) into %u halfspace(s)",
794 file.c_str(), cellIdx, first, count, nHalfspaces);
795 return false;
796 }
797 solid.AddCell(first, count, volume);
798 solid.SetCellBBox(static_cast<int>(cellIdx), lo, hi);
799 }
800
801 return true;
802}
803
804bool WriteFlatCSG(const std::string& file, const O2FlatCSG& solid)
805{
806 // refuse an unclosed shape: its unset cell boxes would read back as zeros and pass validation on reload
807 if (!solid.IsClosed()) {
808 ::Error("WriteFlatCSG",
809 "%s: shape %s is not closed (CloseShape() was never called, or refused); refusing to "
810 "write a sidecar that may encode a degenerate cell box",
811 file.c_str(), solid.GetName());
812 return false;
813 }
814
815 std::ofstream out(file, std::ios::binary);
816 if (!out) {
817 ::Error("WriteFlatCSG", "Cannot open %s for writing", file.c_str());
818 return false;
819 }
820
821 out.write("O2FLTCSG", 8);
822 const uint32_t version = kFlatCSGVersion;
823 const uint32_t nHalfspaces = static_cast<uint32_t>(solid.GetNhalfspaces());
824 const uint32_t nCells = static_cast<uint32_t>(solid.GetNcells());
825 writeValue(out, version);
826 writeValue(out, nHalfspaces);
827 writeValue(out, nCells);
828
829 // field by field, byte-identical to the loader and to cadsupport/flat.py's writer
830 for (uint32_t h = 0; h < nHalfspaces; ++h) {
831 const FlatCSGHalfspace& halfspace = solid.GetHalfspace(static_cast<int>(h));
832 const int32_t kind = halfspace.kind;
833 writeValue(out, kind);
834 writeValue(out, halfspace.sign);
835 for (double value : halfspace.c) {
836 writeValue(out, value);
837 }
838 }
839 for (uint32_t cellIdx = 0; cellIdx < nCells; ++cellIdx) {
840 const FlatCSGCell& cell = solid.GetCell(static_cast<int>(cellIdx));
841 const int32_t first = cell.first;
842 const int32_t count = cell.count;
843 writeValue(out, first);
844 writeValue(out, count);
845 writeValue(out, cell.volume);
846 double lo[3], hi[3];
847 solid.GetCellBBox(static_cast<int>(cellIdx), lo, hi);
848 for (double value : lo) {
849 writeValue(out, value);
850 }
851 for (double value : hi) {
852 writeValue(out, value);
853 }
854 }
855
856 if (!out) {
857 ::Error("WriteFlatCSG", "%s: write failed", file.c_str());
858 return false;
859 }
860 return true;
861}
862
863} // namespace cad
864} // namespace o2
std::vector< o2::soa::IndexRecord > records
Private analytic bounded surfaces, trim wires and closure checks behind O2BVHSurfaceSolid.
unsigned int uint32_t
int32_t i
configStandalone Light OpenGL display display_light Apply smoothing to points Enable Z buffer Highlight links Highlight seeds Highlight sector tracks Highlight final tracks Show individual Show related Highlight grid Show only individual collision Color clusters belonging to track objects Set line Enable drawing TPC data Enable drawing TOF data Enable drawing magnetic field Set field line step count Invert colors configStandalone Heavy OpenGL display display_heavy Show tracks with ITS contribution Show tracks with TOF contribution Enable propagation of loopers Show clusters at nominal x of pad row for early transformed data Split CE tracks when they cross the central Mark clusters Mark adjacent clusters Hide rejected tracks Apply filter on tracks to be displayed Separate increase X h configStandalone Camera window renderer OpenGL display display_camera Orient the camera such that the y axis is always upwards Full Screen MultiSample Anti Downsampling Anti Enable Vertical Sync Target frame or if the backend cannot limit to the refresh Use OpenGL indirect draws to reduce number of draw calls Display h configStandalone Vulkan display display_vulkan Have uniform buffers in host accessible device memory h configStandalone Internal display display_internal Number of updates the Qt GUI may send ahead of the last one the display has before it waits for the display Opacity of the console background Number of console lines the history is shortened to this when it reaches twice the size Time in seconds an info message fades out after setInfoTime h OpenGL display display Font size Do not use Freetype for font Renderer for event Frontend to use for event ROOT macros used as track filter Console script file
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error s
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error First event to Number of events to Number of iterations to Number of iterations to Number of initial iterations excluded from const char Directory with events to Events directory is and not inside events Show standalone event Run event generator maximum time bin of continuous for triggered for automatic continuous for automatic continuous triggered uint64_t
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error First event to Number of events to Number of iterations to Number of iterations to Number of initial iterations excluded from const char e
configStandalone Reconstruction rec_tpc multiplier for the combined cluster track error during track following area in cm for the search of for z only used if decrease removal by when above this local occupancy rowx Shift of TPC revert cluster flag edge bit distance to edge exceeds this ZOffset for secondary multiple of radius max P of electron K BB bands crossing max P of electron p BB bands crossing max P of electron d BB bands crossing max P of electron t BB bands crossing min accepted dEdxMaxTPC of the track Additive extra cluster error for Y2 if edge flag set Additive extra cluster error for Y2 if single set Additive extra cluster error for Y2 if or single set Additive extra cluster error for Z2 if or single set Extra errors to add to track when crossing depending on addErrorsCECrossing Inner portion of y range in sector that is not used in searching for extrapolated track candidates factor2YS for track merging factor2K for track merging Margin in cm of Y position when rejecting edge clusters based on uncorrected track Y Max fraction of shared clusters for tracklet Except pad from cluster finding if total number of charges in a fragment is above this Except pad from cluster finding if number of consecutive charges in a fragment is above this Threshold where a timebin is considered disabling the noisy pad check for that pad Treat signal above this minimum as saturated Smoothing factor for the exponential Highly Ionising Particle tail filter Number of timebins to use for the averaging Add additional custom track errors when crossing relative as fraction of Do N fit passes in final fit of< 0:reject at max -n hits") AddOptionRTC(rejectIFCLowRadiusCluster, uint8_t, 1, "", 0, "Reject clusters that get the IFC mask error during refit") AddOptionRTC(dEdxTruncLow, uint8_t, 2, "", 0, "Low truncation threshold, fraction of 128") AddOptionRTC(dEdxTruncHigh, uint8_t, 77, "", 0, "High truncation threshold, fraction of 128") AddOptionRTC(extrapolationTracking, int8_t, 1, "", 0, "Enable Extrapolation Tracking(prolong tracks to adjacent sectors to find short segments)") AddOptionRTC(disableRefitAttachment, uint8_t, 0, "", 0, "Bitmask to disable certain attachment steps during refit(1:attachment, 2:propagation, 4:loop following, 8:mirroring)") AddOptionRTC(rejectionStrategy, uint8_t, o2::gpu::GPUSettings::RejectionStrategyA, "", 0, "Enable rejection of TPC clusters for compression(0=no, 1=strategy A, 2=strategy B)") AddOptionRTC(mergeLoopersAfterburner, uint8_t, 1, "", 0, "Run afterburner for additional looper merging") AddOptionRTC(compressionTypeMask, uint8_t, o2::gpu::GPUSettings::CompressionFull, "", 0, "TPC Compression mode bits(1=truncate charge/width LSB, 2=differences, 4=track-model)") AddOptionRTC(compressionSortOrder, uint8_t, o2::gpu::GPUSettings::SortTime, "", 0, "Sort order of TPC compression(0=time, 1=pad, 2=Z-time-pad, 3=Z-pad-time, 4=no sorting(use incoming order))") AddOptionRTC(sigBitsCharge, uint8_t, 4, "", 0, "Number of significant bits for TPC cluster charge in compression mode 1") AddOptionRTC(sigBitsWidth, uint8_t, 3, "", 0, "Number of significant bits for TPC cluster width in compression mode 1") AddOptionRTC(dropLoopers, uint8_t, 0, "", 0, "Drop looping tracks starting from second loop") AddOptionRTC(mergerInterpolateErrors, uint8_t, 1, "", 0, "Use interpolation instead of extrapolation for chi2 based cluster rejection") AddOptionRTC(mergerInterpolateRejectAlsoOnCurrentPosition, uint8_t, 1, "", 0, "When using mergerInterpolateErrors, reject based on chi2 twice computed with interpolated and current track position starting from NDF > mergerNonInterpolateRejectMinNDF Merge tracks accross the central electrode Enable PID response Add sub threshold clusters in TPC dEdx computation OR mask of TPC flags that will reject the cluster in alternative dEdx Reject edge clusters based on uncorrected track Y during track fit Increase decrease removal by when on this number of pads from the edge h configStandalone Reconstruction rec_trd Max chi2 for TRD tracklets to be matched to a track Chi2 cut for strict matching mode Number of sigmas for ITS TPC track time error estimate Addition to search road around track prolongation along Y in cm Optimal tracklet rphi resolution in maximum chi2 per attached tracklet for TRD tracks take into account the full likelihood of the slope including exponential tails Tracks with less attached tracklets are discarded after the tracking Post trigger Pile up integration time in BCs fits all iterations but the last one in projections Use material lookup table for TPC refit Apply workarounds to avoid cyclic merge should not be needed tpc dyn configStandalone Processing proc_rtc Replace constant variables static by constexpr expressions Compile RTC in deterministic with NO_FAST_MATH flags and GPUCA_DETERMINISTIC_MODE define Use RTC to optimize GPU code h configStandalone Processing proc_rtctech Use a file lock to serialize access to the cache folder int32_t
constexpr int p2()
constexpr int p1()
constexpr to accelerate the coordinates changing
uint32_t role
std::vector< SidecarEdge > edges
const char * moreThanOneOuter
uint32_t curveType
const char * noOuter
uint32_t surfaceType
uint16_t slope
Definition RawData.h:1
uint32_t c
Definition RawData.h:2
uint32_t version
Definition RawData.h:8
Class for time synchronization of RawReader instances.
bool AddFacet(const Vertex_t &pt0, const Vertex_t &pt1, const Vertex_t &pt2)
Adding a triangular facet from vertex positions in absolute coordinates.
Tessellated::Vertex_t Vertex_t
bool AddCylindricalSurface(const Point3D &centerPoint, const Point3D &axis, const Point3D &referenceAxisU, double radius, double heightMin, double heightMax, double phiStart=0., double phiSweep=6.283185307179586, bool innerWall=false)
Add a cylindrical wall of radius around axis over a height range and a phi sweep; innerWall points th...
std::array< double, 3 > Point3D
std::array< double, 2 > Point2D
bool AddCurvedPlanarSurface(const Point3D &origin, const Point3D &axisU, const Point3D &axisV, const std::vector< PlanarBoundaryCurve > &outerWire, const std::vector< std::vector< PlanarBoundaryCurve > > &innerWires={})
Add an exact planar surface bounded by line/arc wires; axisU and axisV are orthonormal and axisU x ax...
bool AddConicalSurface(const Point3D &centerPoint, const Point3D &axis, const Point3D &referenceAxisU, double radiusAtMin, double radiusAtMax, double heightMin, double heightMax, double phiStart=0., double phiSweep=6.283185307179586, bool innerWall=false)
Add a conical wall whose radius runs linearly from radiusAtMin to radiusAtMax; one radius may be zero...
void SetModelTolerance(double toleranceCm)
bool SetSurfaceBoundaryEdges(int surfaceIndex, const std::vector< unsigned int > &edgeIds, const std::vector< unsigned char > &edgeFlags)
Attach surface surfaceIndex's edge identities in trim-curve order; false on a bad index or mismatched...
bool AddSphericalSurface(const Point3D &center, const Point3D &polarAxis, const Point3D &referenceAxisU, double radius, double thetaMin=0., double thetaMax=3.141592653589793, double phiStart=0., double phiSweep=6.283185307179586, bool innerWall=false)
Add a spherical surface of radius trimmed to a theta range and a phi sweep; the defaults give a full ...
bool AddToroidalSurface(const Point3D &centerPoint, const Point3D &axis, const Point3D &referenceAxisU, double majorRadius, double minorRadius, double phiStart=0., double phiSweep=6.283185307179586, double tubeStart=0., double tubeSweep=6.283185307179586, bool innerWall=false)
Add a toroidal surface trimmed to a phiRing x phiTube rectangle; the defaults give a full torus,...
bool AddPlanarSurface(const Point3D &origin, const Point3D &axisU, const Point3D &axisV, const std::vector< Point2D > &outerWire, const std::vector< std::vector< Point2D > > &innerWires={})
int AddCell(int first, int count, double volume)
Append a cell over [first, first + count) of the halfspace array; returns its index.
int AddQuadric(double sign, const double coeff[10])
Append a quadric halfspace; returns its index. sign is +1 or -1, inside is sign*Q <= 0.
int GetNhalfspaces() const
Definition O2FlatCSG.h:75
const FlatCSGCell & GetCell(int index) const
Definition O2FlatCSG.h:78
int GetNcells() const
Definition O2FlatCSG.h:76
int AddTorus(double sign, const double *centre, const double *axis, double major, double minor)
Append a torus halfspace, inside sign * (sqrt((rho - major)^2 + z^2) - minor) <= 0 about unit axis; r...
bool IsClosed() const
Definition O2FlatCSG.h:89
void GetCellBBox(int cell, double *lo, double *hi) const
const FlatCSGHalfspace & GetHalfspace(int index) const
Definition O2FlatCSG.h:77
void SetCellBBox(int cell, const double *lo, const double *hi)
GLdouble n
Definition glcorearb.h:1982
GLsizei const GLuint const GLfloat * weights
Definition glcorearb.h:5475
GLint GLsizei count
Definition glcorearb.h:399
GLuint GLuint end
Definition glcorearb.h:469
const GLdouble * v
Definition glcorearb.h:832
GLuint const GLchar * name
Definition glcorearb.h:781
GLint first
Definition glcorearb.h:399
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLenum const GLfloat * params
Definition glcorearb.h:272
GLenum GLsizei GLsizei GLint * values
Definition glcorearb.h:1576
GLintptr offset
Definition glcorearb.h:660
GLubyte GLubyte GLubyte GLubyte w
Definition glcorearb.h:852
constexpr double wireJoinToleranceFor(double modelTolerance)
The wire-join band for a model with a declared tolerance: that tolerance when looser than kWireJoinTo...
void coneParametricMetric(double radiusAtHeight, double slope, double &gUU, double &gUV, double &gVV)
void planeParametricMetric(const Vec3 &axisU, const Vec3 &axisV, double &gUU, double &gUV, double &gVV)
constexpr double kWireJoinTolerance
Wire-closure tolerance, a 3D length in cm through the surface metric: the CAD extractor's endpoint pr...
void torusParametricMetric(double majorRadius, double minorRadius, double phiTube, double &gUU, double &gUV, double &gVV)
Torus, (u, v) = (phiRing[rad], phiTube[rad]). The ring scale runs from R - r to R + r.
void cylinderParametricMetric(double radius, double &gUU, double &gUV, double &gVV)
Cylinder, (u, v) = (phi[rad], h[cm]).
void sphereParametricMetric(double radius, double theta, double &gUU, double &gUV, double &gVV)
bool LoadFlatCSG(const std::string &file, O2FlatCSG &solid)
Load a flat-CSG sidecar (flatcsg_*.bin, version 1) into solid; call CloseShape() after....
bool LoadFacetSolid(const std::string &file, o2::base::O2Tessellated &solid)
bool WriteFlatCSG(const std::string &file, const O2FlatCSG &solid)
bool LoadSurfaceSolid(const std::string &file, O2BVHSurfaceSolid &solid)
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
One DNF cell: [first, first + count) of the halfspace array, intersected; volume is its own volume.
Definition O2FlatCSG.h:37
static PlanarBoundaryCurve makeBSpline(int splineDegree, std::vector< Point2D > splinePoles, std::vector< double > splineWeights, std::vector< double > splineKnots)
static Curve2D makeBSpline(int splineDegree, std::vector< Vec2 > splinePoles, std::vector< double > splineWeights, std::vector< double > splineKnots)
std::map< std::string, ID > expected