Belle II Software development
BelleLathe.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/* Own header. */
10#include <ecl/geometry/BelleLathe.h>
11
12/* Geant4 headers. */
13#include <G4AffineTransform.hh>
14#include <G4VoxelLimits.hh>
15#include <G4VGraphicsScene.hh>
16#include <G4VPVParameterisation.hh>
17
18/* CLHEP headers. */
19#include <CLHEP/Random/RandFlat.h>
20
21using namespace std;
22using namespace Belle2;
23using namespace ECL;
24
25#define COMPARE 0
26#define PERFCOUNTER 0
27#if PERFCOUNTER==1
28typedef int counter_t[6];
29map<string, counter_t> counterl;
30#define COUNTER(x) counterl[GetName()][x]++
31//#define MATCHOUT(x) //if(GetName().find("sv_crystalcontainersolid")==0) cout<<x<<endl;
32#else
33#define COUNTER(x)
34//
35#endif
36
37//#define MATCHOUT(x) G4cout<<GetName()<<" "<<x<<G4endl;
38#define MATCHOUT(x)
39
41struct Plane_t {
42 G4ThreeVector n;
43 double d;
44 // => n.x*x + n.y*y + n.z*z + d = 0
45};
46
47namespace Belle2 {
52 namespace ECL {
53 inline double dotxy(const G4ThreeVector& p, const G4ThreeVector& n)
54 {
55 return p.x() * n.x() + p.y() * n.y();
56 }
57 }
59}
60
61ostream& operator <<(ostream& o, const zr_t& v)
62{
63 return o << "{" << v.z << ", " << v.r << "}";
64}
65
67struct curl_t {
68 G4ThreeVector v;
70 explicit curl_t(const G4ThreeVector& _v): v(_v) {}
71};
72
73ostream& operator <<(ostream& o, const curl_t& c)
74{
75 return o << "{" << c.v.x() << ", " << c.v.y() << ", " << c.v.z() << "}, ";
76}
77
78
79BelleLathe::BelleLathe(const G4String& pName, double phi0, double dphi, const vector<zr_t>& c)
80 : G4CSGSolid(pName)
81{
82 Init(c, phi0, dphi);
83}
84
85BelleLathe::BelleLathe(const G4String& pName, double phi0, double dphi, int n, double* z, double* rin, double* rout)
86 : G4CSGSolid(pName)
87{
88 vector<zr_t> contour;
89 for (int i = 0; i < n; i++) {
90 zr_t t = {z[i], rin[i]};
91 contour.push_back(t);
92 }
93 for (int i = n - 1; i >= 0; i--) {
94 zr_t t = {z[i], rout[i]};
95 contour.push_back(t);
96 }
97
98 Init(contour, phi0, dphi);
99}
100
101void BelleLathe::Init(const vector<zr_t>& c, double phi0, double dphi)
102{
103 vector<zr_t> contour = c;
104 // remove duplicated vertices
105 do {
106 vector<zr_t>::iterator it0 = contour.begin(), it1 = it0 + 1;
107 for (; it1 != contour.end();) {
108 const zr_t& s0 = *it0, &s1 = *it1;
109 if (abs(s0.z - s1.z) < kCarTolerance && abs(s0.r - s1.r) < kCarTolerance)
110 it1 = contour.erase(it1);
111 else {
112 ++it0; ++it1;
113 }
114 }
115 // cppcheck-suppress containerOutOfBounds ; contour should be valid here
116 const zr_t& s0 = *it0, &s1 = contour[0]; // cppcheck-suppress invalidContainer ; contour should be valid here
117 if (abs(s0.z - s1.z) < kCarTolerance && abs(s0.r - s1.r) < kCarTolerance) contour.erase(it0);
118 } while (0);
119
120 // remove vertices on the same line
121 do {
122 vector<zr_t>::iterator it0 = contour.begin(), it1 = it0 + 1, it2 = it1 + 1;
123 for (; it0 != contour.end();) {
124 const zr_t& s0 = *it0, &s1 = *it1, &s2 = *it2;
125 double dr2 = s2.r - s0.r, dz2 = s2.z - s0.z;
126 double d = (s1.z - s0.z) * dr2 - (s1.r - s0.r) * dz2;
127
128 if (d * d < kCarTolerance * kCarTolerance * (dr2 * dr2 + dz2 * dz2)) {
129 it1 = contour.erase(it1);
130 it2 = it1;
131 if (++it2 >= contour.end()) it2 = contour.begin();
132 it0 = it1;
133 if (--it0 < contour.begin()) it0 = (++contour.rbegin()).base();
134
135 } else {
136 ++it0;
137 if (++it1 >= contour.end()) it1 = contour.begin();
138 if (++it2 >= contour.end()) it2 = contour.begin();
139 }
140
141 }
142 } while (0);
143
144 double sum = 0;
145 zr_t p0 = contour[0];
146 for (int i = 1, imax = contour.size(); i < imax; i++) {
147 zr_t p1 = contour[i];
148 sum += (p1.z - p0.z) * (p1.r + p0.r);
149 p0 = p1;
150 }
151 zr_t p1 = contour[0];
152 sum += (p1.z - p0.z) * (p1.r + p0.r);
153
154 // If contour is Clockwise: reverse contour
155 if (sum > 0)
156 std::reverse(contour.begin(), contour.end());
157
158 fcontour = contour;
159
160 auto convexside = [this](cachezr_t& s, double eps) -> void {
161 s.isconvex = false;
162 if (s.dz > 0) return;
163 vector<zr_t>::const_iterator it = fcontour.begin();
164 double a = s.dz * s.is, b = s.dr * s.is, cc = b * s.z - a * s.r;
165 bool dp = false, dm = false;
166 s.isconvex = true;
167 do
168 {
169 const zr_t& p = *it;
170 double d = a * p.r - b * p.z + cc; // distance to line
171 dm = dm || (d < -eps);
172 dp = dp || (d > eps);
173 if (dm && dp) {s.isconvex = false; return;}
174 } while (++it != fcontour.end());
175 };
176
177 frmin = kInfinity;
178 frmax = -kInfinity;
179 fzmin = kInfinity;
180 fzmax = -kInfinity;
181 fcache.reserve(fcontour.size());
182 for (int i = 0, n = fcontour.size(); i < n; i++) {
183 const zr_t& s0 = fcontour[i], &s1 = fcontour[(i + 1) % n];
184 cachezr_t t;
185 t.z = s0.z;
186 t.r = s0.r;
187 t.dz = s1.z - s0.z;
188 t.dr = s1.r - s0.r;
189 t.s2 = t.dz * t.dz + t.dr * t.dr;
190 t.is2 = 1 / t.s2;
191 t.is = sqrt(t.is2);
192 t.zmin = min(s0.z, s1.z);
193 t.zmax = max(s0.z, s1.z);
194 t.r2min = pow(min(s0.r, s1.r), 2);
195 t.r2max = pow(max(s0.r, s1.r), 2);
196 t.ta = (s1.r - s0.r) / (s1.z - s0.z);
197 convexside(t, kCarTolerance);
198 fcache.push_back(t);
199
200 frmax = max(frmax, s0.r);
201 frmin = min(frmin, s0.r);
202 fzmax = max(fzmax, s0.z);
203 fzmin = min(fzmin, s0.z);
204 }
205
206 fphi = phi0;
207 fdphi = dphi;
208
209 fdphi = std::min(2 * M_PI, fdphi);
210 fdphi = std::max(0.0, fdphi);
211 fc0 = cos(fphi);
212 fs0 = sin(fphi);
213 fc1 = cos(fphi + fdphi);
214 fs1 = sin(fphi + fdphi);
215
216 fn0x = fs0;
217 fn0y = -fc0;
218 fn1x = -fs1;
219 fn1y = fc1;
220 fgtpi = fdphi > M_PI;
221 ftwopi = abs(fdphi - 2 * M_PI) < kCarTolerance;
222
223 // cout << ftwopi << " " << fgtpi << " " << fn0y << " " << fn0x << " " << fn1y << " " << fn1x << endl;
224
225 for (int i = 0, n = fcontour.size(); i < n; i++) {
226 const zr_t& s = fcontour[i];
227 fz.push_back(s.z);
228 }
229 sort(fz.begin(), fz.end());
230 fz.erase(std::unique(fz.begin(), fz.end()), fz.end());
231
232 for (int i = 1, ni = fz.size(); i < ni; i++) {
233 double a = fz[i - 1], b = fz[i];
234 findx.push_back(fseg.size());
235 for (int j = 0, nj = fcache.size(); j < nj; j++) {
236 const cachezr_t& sj = fcache[j];
237 double cc = sj.zmin, d = sj.zmax;
238 if (cc != d and b > cc and d > a) { // overlap
239 fseg.push_back(j);
240 }
241 }
242 }
243 findx.push_back(fseg.size());
244
245 getvolarea();
246
247#if COMPARE>0
248 auto getpolycone = [](const G4String & pName, double phi0, double dphi, const vector<zr_t>& c) -> G4GenericPolycone* {
249 vector<double> r, z;
250 r.reserve(c.size());
251 z.reserve(c.size());
252 for (int i = 0, imax = c.size(); i < imax; i++)
253 {
254 r.push_back(c[i].r);
255 z.push_back(c[i].z);
256 }
257 return new G4GenericPolycone(pName, phi0, dphi, c.size(), r.data(), z.data());
258 };
259 fshape = getpolycone(GetName(), phi0, dphi, fcontour);
260#else
261 fshape = nullptr;
262#endif
263// StreamInfo(G4cout);
264}
265
266// Nominal constructor for BelleLathe whose parameters are to be set by
267// a G4VParamaterisation later. Check and set half-widths as well as
268// angles: final check of coplanarity
269BelleLathe::BelleLathe(const G4String& pName)
270 : G4CSGSolid(pName)
271{
272 vector<zr_t> a;
273 Init(a, 0, 2 * M_PI);
274}
275
276// Fake default constructor - sets only member data and allocates memory
277// for usage restricted to object persistency.
279 : G4CSGSolid(a)
280{
281 vector<zr_t> b;
282 Init(b, 0, 2 * M_PI);
283}
284
285// Destructor
287{
288#if PERFCOUNTER==1
289 cout << GetName() << " ";
290 for (int i = 0; i < 6; i++) cout << counterl[GetName()][i] << " "; cout << endl;
291#endif
292}
293
294// Copy constructor
296 : G4CSGSolid(rhs), fcontour(rhs.fcontour), fcache(rhs.fcache), fz(rhs.fz),
297 findx(rhs.findx), fseg(rhs.fseg), farea(rhs.farea), ftlist(rhs.ftlist),
298 fphi(rhs.fphi), fdphi(rhs.fdphi), fs0(rhs.fs0), fc0(rhs.fc0), fs1(rhs.fs1),
299 fc1(rhs.fc1), fn0x(rhs.fn0x), fn0y(rhs.fn0y), fn1x(rhs.fn1x), fn1y(rhs.fn1y),
300 frmin(rhs.frmin), frmax(rhs.frmax), fzmin(rhs.fzmin), fzmax(rhs.fzmax),
301 fgtpi(rhs.fgtpi), ftwopi(rhs.ftwopi), fshape(rhs.fshape), fsurf(rhs.fsurf)
302{
303}
304
305// Assignment operator
307{
308 // Check assignment to self
309 if (this == &rhs) { return *this; }
310
311 // Copy base class data
312 G4CSGSolid::operator=(rhs);
313
314 // Copy data
315 fcontour = rhs.fcontour;
316 fcache = rhs.fcache;
317 fz = rhs.fz;
318 findx = rhs.findx;
319 fseg = rhs.fseg;
320 farea = rhs.farea;
321 ftlist = rhs.ftlist;
322 fphi = rhs.fphi;
323 fdphi = rhs.fdphi;
324 fs0 = rhs.fs0;
325 fc0 = rhs.fc0;
326 fs1 = rhs.fs1;
327 fc1 = rhs.fc1;
328 fn0x = rhs.fn0x;
329 fn0y = rhs.fn0y;
330 fn1x = rhs.fn1x;
331 fn1y = rhs.fn1y;
332 frmin = rhs.frmin;
333 frmax = rhs.frmax;
334 fzmin = rhs.fzmin;
335 fzmax = rhs.fzmax;
336 fgtpi = rhs.fgtpi;
337 ftwopi = rhs.ftwopi;
338 fshape = rhs.fshape;
339 fsurf = rhs.fsurf;
340 return *this;
341}
342
343
344// Dispatch to parameterisation for replication mechanism dimension
345// computation & modification.
346void BelleLathe::ComputeDimensions(G4VPVParameterisation*,
347 const G4int,
348 const G4VPhysicalVolume*)
349{
350 G4Exception("BelleLathe::ComputeDimensions()",
351 "GeomSolids0001", FatalException,
352 "BelleLathe does not support Parameterisation.");
353 // std::cout<<"ComputeDimensions"<<std::endl;
354 // p->ComputeDimensions(*this,n,pRep);
355}
356
357vector<double> quadsolve(double a, double b, double c)
358{
359 // solve equation a*t^2 + b*t + c = 0 taking care intermediate rounding errors
360 vector<double> t(2);
361 b *= 0.5;
362 double D = b * b - a * c;
363 if (D >= 0) {
364 double sD = sqrt(D);
365 double sum = b + ((b > 0) ? sD : -sD);
366 double t0 = -c / sum;
367 double t1 = -sum / a;
368 t[0] = t0;
369 t[1] = t1;
370 } else {
371 t.clear();
372 }
373
374 return t;
375}
376
377inline int quadsolve(double a, double b, double c, double& t0, double& t1)
378{
379 // solve equation a*t^2 + b*t + c = 0 taking care intermediate rounding errors
380 b *= 0.5;
381 double D = b * b - a * c;
382 if (D >= 0) {
383 double sD = sqrt(D);
384 double sum = b + ((b > 0) ? sD : -sD);
385 t0 = -c / sum;
386 t1 = -sum / a;
387 return 2;
388 }
389 return 0;
390}
391
394 double t;
395 double s;
396};
397vector<solution_t> extremum(double A, double B, double C, double D, double E, double F)
398{
399 // extremum of Fun(t,s) = A*t*t + B*t*s + C*s*s + D*t + E*s + F => dFun/ds = 0
400 vector<solution_t> res;
401 if (abs(B) < abs(A)) {
402 double a = 4 * A * C - B * B;
403 double b = 2 * (2 * A * E - B * D);
404 double c = 4 * A * F - D * D;
405 vector<double> ss = quadsolve(a, b, c);
406 for (auto s : ss) {
407 if (fpclassify(s) == FP_INFINITE) continue;
408 double t = -(s * B + D) / (2 * A);
409 solution_t r = {t, s};
410 res.push_back(r);
411 }
412 } else {
413 double B2 = B * B, CD = C * D, BE = B * E;
414 double a = A * (4 * A * C - B2);
415 double b = 2 * A * (2 * CD - BE);
416 double c = D * (CD - BE) + B2 * F;
417 vector<double> ts = quadsolve(a, b, c);
418 for (auto t : ts) {
419 if (fpclassify(t) == FP_INFINITE) continue;
420 double s = -(2 * t * A + D) / B;
421 solution_t r = {t, s};
422 res.push_back(r);
423 }
424 }
425 return res;
426}
427
428// calculate all ray solid's surface intersection return ordered vector
429vector<double> BelleLathe::linecross(const G4ThreeVector& p, const G4ThreeVector& n) const
430{
431 auto hitside = [this, &p, &n](double t, double zmin, double zmax) -> bool {
432 double z = p.z() + n.z() * t;
433 bool k = zmin < z && z <= zmax;
434 if (k && !ftwopi)
435 {
436 double x = p.x() + n.x() * t;
437 double y = p.y() + n.y() * t;
438 k = k && insector(x, y);
439 }
440 return k;
441 };
442
443 auto hitzside = [this, &p, &n](double t, double r2min, double r2max) -> bool {
444 double x = p.x() + n.x() * t;
445 double y = p.y() + n.y() * t;
446 double r2 = x * x + y * y;
447 bool k = r2min <= r2 && r2 < r2max;
448 if (k && !ftwopi)
449 {
450 k = k && insector(x, y);
451 }
452 return k;
453 };
454
455 vector<double> tc;
456 double inz = 1 / n.z();
457 double nn = Belle2::ECL::dotxy(n, n), np = dotxy(n, p), pp = dotxy(p, p);
458 for (const cachezr_t& s : fcache) { // loop over sides
459 if (s.dz == 0.0) { // z-plane
460 double t = (s.z - p.z()) * inz;
461 if (hitzside(t, s.r2min, s.r2max)) { tc.push_back(t); }
462 } else {
463 double ta = s.ta;
464 double A, B, R2;
465 if (s.dr == 0.0) { // cylinder
466 double R = s.r;
467 R2 = R * R;
468
469 A = -nn;
470 B = np;
471 } else { // cone
472 double taz = ta * (p.z() - s.z);
473 double R = taz + s.r;
474 R2 = R * R;
475
476 double nzta = n.z() * ta;
477 A = nzta * nzta - nn;
478 B = np - nzta * R;
479 }
480 double D = B * B + (pp - R2) * A;
481 if (D > 0) {
482 double sD = sqrt(D), iA = 1 / A;
483 double t0 = (B + sD) * iA, t1 = (B - sD) * iA;
484 if (hitside(t0, s.zmin, s.zmax)) tc.push_back(t0);
485 if (hitside(t1, s.zmin, s.zmax)) tc.push_back(t1);
486 }
487 }
488 }
489
490 if (!ftwopi) {
491 do { // side at phi0
492 double d = fn0x * p.x() + fn0y * p.y();
493 double vn = fn0x * n.x() + fn0y * n.y();
494 double t = -d / vn;
495 G4ThreeVector r = p + n * t;
496 zr_t zr = {r.z(), fc0 * r.x() + fs0 * r.y()};
497 if (vn != 0 && wn_poly(zr) == 2) tc.push_back(t);
498 } while (0);
499
500 do { // side at phi0+dphi
501 double d = fn1x * p.x() + fn1y * p.y();
502 double vn = fn1x * n.x() + fn1y * n.y();
503 double t = -d / vn;
504 G4ThreeVector r = p + n * t;
505 zr_t zr = {r.z(), fc1 * r.x() + fs1 * r.y()};
506 if (vn != 0 && wn_poly(zr) == 2) tc.push_back(t);
507 } while (0);
508 }
509
510 sort(tc.begin(), tc.end());
511 return tc;
512}
513
514// Calculate extent under transform and specified limit
515G4bool BelleLathe::CalculateExtent(const EAxis A,
516 const G4VoxelLimits& bb,
517 const G4AffineTransform& T,
518 G4double& pMin, G4double& pMax) const
519{
520 auto maxdist = [this](const G4ThreeVector & n) -> G4ThreeVector {
521 G4ThreeVector r;
522 int i = 0, nsize = fcache.size();
523 if (ftwopi || insector(n.x(), n.y())) // n in sector
524 {
525 double nr = hypot(n.x(), n.y()), nz = n.z();
526 double dmax = -kInfinity, R = 0, Z = 0;
527 do {
528 const cachezr_t& s = fcache[i];
529 double d1 = nz * s.z + nr * s.r;
530 if (dmax < d1) { R = s.r; Z = s.z; dmax = d1;}
531 } while (++i < nsize);
532 if (nr > 0) {
533 R /= nr;
534 r.set(R * n.x(), R * n.y(), Z);
535 } else {
536 double phi = fphi + 0.5 * fdphi;
537 r.set(R * cos(phi), R * sin(phi), Z);
538 }
539 } else
540 {
541 double dmax = -kInfinity;
542 do {
543 const cachezr_t& s = fcache[i];
544 // check both sides
545 G4ThreeVector rf(-fn0y * s.r, fn0x * s.r, s.z), rl(fn1y * s.r, -fn1x * s.r, s.z);
546 double d0 = rf * n, d1 = rl * n;
547 // cout<<rf<<" "<<rl<<endl;
548 if (dmax < d0) { r = rf; dmax = d0;}
549 if (dmax < d1) { r = rl; dmax = d1;}
550 } while (++i < nsize);
551 }
552 return r;
553 };
554
555 struct seg_t {int i0, i1;};
556 auto clip = [](vector<G4ThreeVector>& vlist, vector<seg_t>& slist, const G4ThreeVector & n, double dist) {
557 vector<seg_t> snew;
558 vector<int> lone;
559
560 vector<double> d;
561 for (const G4ThreeVector& v : vlist) d.push_back(v * n + dist);
562
563 for (seg_t s : slist) {
564 double prod = d[s.i0] * d[s.i1];
565 // cout<<d[s.i0]<<" "<<d[s.i1]<<endl;
566 if (prod < 0) { // segment crosses plane - break it
567 G4ThreeVector rn = (vlist[s.i0] * d[s.i1] - vlist[s.i1] * d[s.i0]) * (1 / (d[s.i1] - d[s.i0]));
568 lone.push_back(vlist.size()); vlist.push_back(rn);
569 if (d[s.i0] < 0) {
570 s = {lone.back(), s.i1};
571 } else {
572 s = {s.i0, lone.back()};
573 }
574 } else if (prod == 0) { // segment end on plane
575 if (d[s.i0] == 0 && d[s.i1] > 0) {
576 lone.push_back(s.i0);
577 } else if (d[s.i0] > 0 && d[s.i1] == 0) {
578 lone.push_back(s.i1);
579 } else continue;
580 } else {
581 if (d[s.i0] < 0) continue; // segment below plane
582 }
583 snew.push_back(s);
584 }
585
586 double dmax = -1e99;
587 int imax = -1, jmax = -1;
588 // search for the most distant points on the clipping plane
589 for (unsigned int i = 0; i < lone.size(); i++) {
590 for (unsigned int j = i + 1; j < lone.size(); j++) {
591 double d2 = (vlist[lone[i]] - vlist[lone[j]]).mag2();
592 if (d2 > dmax) { imax = lone[i]; jmax = lone[j];}
593 }
594 }
595
596 // close the new polygon by creating new segments
597 if (imax >= 0) {
598 G4ThreeVector k = vlist[jmax] - vlist[imax];
599 sort(lone.begin(), lone.end(), [&k, &vlist, &imax](int i, int j) {return k * (vlist[i] - vlist[imax]) < k * (vlist[j] - vlist[imax]);});
600
601 for (unsigned int i = 0; i < lone.size(); i += 2) {
602 seg_t t = {lone[i], lone[i + 1]};
603 for (const seg_t& s : snew) {
604 if (t.i1 == s.i0) { snew.push_back(t); break;}
605 if (t.i0 == s.i0) { swap(t.i0, t.i1); snew.push_back(t); break;}
606 }
607 }
608 }
609 swap(slist, snew);
610 };
611
612 auto PhiCrossN = [this, clip](const vector<Plane_t>& planes) {
613 // unordered clipped phi-sides vertices within
614 // limiting planes
615 vector<G4ThreeVector> vlist; // vertex list
616 vector<seg_t> slist; // segment list
617 vector<G4ThreeVector> res;
618
619 int nsize = fcache.size();
620 vlist.reserve(nsize);
621 slist.reserve(nsize);
622 for (int iphi = 0; iphi < 2; iphi++) {
623 vlist.clear();
624 slist.clear();
625 // phi-side directional vector is (kx,ky)
626 double kx = iphi ? -fn0y : fn1y, ky = iphi ? fn0x : -fn1x;
627 do {
628 int i = 0;
629 do {
630 const cachezr_t& s = fcache[i];
631 G4ThreeVector r(kx * s.r, ky * s.r, s.z);
632 vlist.push_back(r);
633 seg_t t = {i, i + 1};
634 slist.push_back(t);
635 } while (++i < nsize - 1);
636 const cachezr_t& s = fcache[nsize - 1];
637 G4ThreeVector r(kx * s.r, ky * s.r, s.z);
638 vlist.push_back(r);
639 seg_t t = {nsize - 1, 0};
640 slist.push_back(t);
641 } while (0);
642
643 // clip phi-side polygon by limiting planes
644 for (const Plane_t& p : planes) {
645 // cout<<p.n<<" "<<p.d<<endl;
646 clip(vlist, slist, p.n, p.d);
647 // for(auto t:vlist) cout<<t<<" "; cout<<endl;
648 }
649 vector<bool> bv(vlist.size(), false);
650
651 for (vector<seg_t>::const_iterator it = slist.begin(); it != slist.end(); ++it) {
652 bv[(*it).i0] = true;
653 bv[(*it).i1] = true;
654 }
655
656 for (unsigned int i = 0; i < vlist.size(); i++) {
657 if (!bv[i]) continue;
658 res.push_back(vlist[i]);
659 }
660 }
661 return res;
662 };
663
664 auto RCross = [this](const G4ThreeVector & op, const G4ThreeVector & k, const G4ThreeVector & u) {
665 // plane with origin at op and normal vector n = [k x u], k and u are orthogonal k*u = 0
666 // plane equation r = t*k + s*u + op
667 vector<solution_t> ts;
668 int nsize = fcache.size();
669 int i = 0;
670 do {
671 const cachezr_t& seg = fcache[i];
672 // r0 -- cone radius at z0, c -- cone axis
673 // cone equation is (r0 + tg * ((r-c0)*c))^2 = (r-c0)^2 - ((r-c0)*c)^2
674 double r0 = seg.r, z0 = seg.z, tg = seg.ta;
675 double rtg = r0 * tg;
676
677 G4ThreeVector o(op.x(), op.y(), op.z() - z0);
678
679 double ko = k * o, uo = u * o, ck = k.z(), cu = u.z(), co = o.z();
680 double k2 = 1, u2 = 1, o2 = o * o;
681 double ck2 = ck * ck, cu2 = cu * cu;
682 double dr2 = r0 * r0 - o2;
683 if (seg.dz != 0.0) {
684 double tg2 = tg * tg, co2 = co * co;
685 double q0 = 1 + tg2;
686 double q1 = co * q0 + rtg;
687
688 double F00 = co2 * q0 + 2 * co * rtg + dr2;
689 double F10 = 2 * (ck * q1 - ko);
690 double F20 = ck2 * q0 - k2;
691 double F01 = 2 * (cu * q1 - uo);
692 double F11 = 2 * ck * cu * q0;
693 double F02 = cu2 * q0 - u2;
694
695 vector<solution_t> res = extremum(F02, F11, F20, F01, F10, F00);
696 for (const solution_t& r : res) {
697 double t = r.s, s = r.t;
698 G4ThreeVector p = t * k + s * u + op;
699 if (seg.zmin < p.z() && p.z() < seg.zmax) {
700 solution_t e = {t, s};
701 if (ftwopi || insector(p.x(), p.y()))
702 ts.push_back(e);
703 }
704 }
705 }
706 double a = -(ck2 * u2 + cu2 * k2);
707 if (a != 0) {
708 if (abs(cu) > abs(ck)) {
709 double b = 2 * (ck * (cu * uo - co * u2) - cu2 * ko);
710 double c = co * (2 * cu * uo - co * u2) + cu2 * dr2;
711 vector<double> tv = quadsolve(a, b, c);
712 for (double t : tv) {
713 double s = -(co + ck * t) / cu;
714 G4ThreeVector p = t * k + s * u + op;
715 if (ftwopi || insector(p.x(), p.y())) {
716 solution_t e = {t, s};
717 ts.push_back(e);
718 }
719 }
720 } else {
721 double b = 2 * (cu * (ck * ko - co * k2) - ck2 * uo);
722 double c = co * (2 * ck * ko - co * k2) + ck2 * dr2;
723 vector<double> sv = quadsolve(a, b, c);
724 for (double s : sv) {
725 double t = -(co + cu * s) / ck;
726 G4ThreeVector p = t * k + s * u + op;
727 if (ftwopi || insector(p.x(), p.y())) {
728 solution_t e = {t, s};
729 ts.push_back(e);
730 }
731 }
732 }
733 }
734 } while (++i < nsize);
735 return ts;
736 };
737
738 bool b1 = false, b2 = false;
739 G4ThreeVector n0, n1, n2;
740 switch (A) {
741 case kXAxis: n0.set(1, 0, 0); n1.set(0, 1, 0); n2.set(0, 0, 1); b1 = bb.IsYLimited(); b2 = bb.IsZLimited(); break;
742 case kYAxis: n0.set(0, 1, 0); n1.set(1, 0, 0); n2.set(0, 0, 1); b1 = bb.IsXLimited(); b2 = bb.IsZLimited(); break;
743 case kZAxis: n0.set(0, 0, 1); n1.set(1, 0, 0); n2.set(0, 1, 0); b1 = bb.IsXLimited(); b2 = bb.IsYLimited(); break;
744 default: break;
745 }
746
747 double dmin1 = -kInfinity, dmax1 = kInfinity;
748 if (b1) {
749 switch (A) {
750 case kXAxis: dmin1 = bb.GetMinYExtent(); dmax1 = bb.GetMaxYExtent(); break;
751 case kYAxis: dmin1 = bb.GetMinXExtent(); dmax1 = bb.GetMaxXExtent(); break;
752 case kZAxis: dmin1 = bb.GetMinXExtent(); dmax1 = bb.GetMaxXExtent(); break;
753 default: break;
754 }
755 }
756
757 double dmin2 = -kInfinity, dmax2 = kInfinity;
758 if (b2) {
759 switch (A) {
760 case kXAxis: dmin2 = bb.GetMinZExtent(); dmax2 = bb.GetMaxZExtent(); break;
761 case kYAxis: dmin2 = bb.GetMinZExtent(); dmax2 = bb.GetMaxZExtent(); break;
762 case kZAxis: dmin2 = bb.GetMinYExtent(); dmax2 = bb.GetMaxYExtent(); break;
763 default: break;
764 }
765 }
766
767 G4AffineTransform iT = T.Inverse();
768 // axis to solid coordinates
769 G4ThreeVector n0t = iT.TransformAxis(n0);
770 G4ThreeVector smin = n0t * kInfinity, smax = (-kInfinity) * n0t; // extremum points in solid coordinate system
771 double pmin = kInfinity, pmax = -pmin;
772 if (b1 && b2) {
773 G4ThreeVector corners[] = {n1* dmin1 + n2 * dmin2, n1* dmax1 + n2 * dmin2, n1* dmax1 + n2 * dmax2, n1* dmin1 + n2 * dmax2};
774 for (G4ThreeVector& c : corners) iT.ApplyPointTransform(c); // to solid coordinates
775
776 vector<Plane_t> planes;
777 for (int i = 0; i < 4; i++) {
778 const G4ThreeVector& c0 = corners[i], &c1 = corners[(i + 1) % 4];
779 vector<double> dists = linecross(c0, n0t);
780 // cout<<"c0 "<<c0<<endl;
781 for (double t : dists) {
782 G4ThreeVector p = n0t * t + c0;
783 double tt = t + c0 * n0t;
784 // cout<<p<<" "<<tt<<endl;
785 if (pmax < tt) { pmax = tt; smax = p;}
786 if (pmin > tt) { pmin = tt; smin = p;}
787 }
788
789 G4ThreeVector u = c1 - c0, un = u.unit();
790 vector<solution_t> ts = RCross(c0, n0t, un);
791 double umax = u.mag();
792 for (solution_t r : ts) {
793 if (0 < r.s && r.s < umax) {
794 double tt = r.t + c0 * n0t;
795 G4ThreeVector p = n0t * r.t + un * r.s + c0;
796 // cout<<r.t<<" "<<r.s<<" "<<smax<<endl;
797 if (pmax < tt) { pmax = tt; smax = p;}
798 if (pmin > tt) { pmin = tt; smin = p;}
799 }
800 }
801 planes.push_back({ -un, un * c1});
802 }
803
804 vector<G4ThreeVector> vside = PhiCrossN(planes);
805 for (const G4ThreeVector& p : vside) {
806 // cout<<p<<endl;
807 double tt = n0t * p;
808 if (pmax < tt) { pmax = tt; smax = p;}
809 if (pmin > tt) { pmin = tt; smin = p;}
810 }
811
812 } else if (b1 || b2) {
813 G4ThreeVector limits[2], u;
814 if (b1) {
815 limits[0] = n1 * dmin1;
816 limits[1] = n1 * dmax1;
817 u = iT.TransformAxis(n2);
818 } else {
819 limits[0] = n2 * dmin2;
820 limits[1] = n2 * dmax2;
821 u = iT.TransformAxis(n1);
822 }
823
824 for (G4ThreeVector& c : limits) iT.ApplyPointTransform(c); // to solid coordinates
825 for (int i = 0; i < 2; i++) {
826 vector<solution_t> ts = RCross(limits[i], n0t, u);
827 for (solution_t r : ts) {
828 double tt = r.t + limits[i] * n0t;
829 G4ThreeVector p = n0t * r.t + u * r.s + limits[i];
830 // cout<<r.t<<" "<<r.s<<" "<<endl;
831 if (pmax < tt) { pmax = tt; smax = p;}
832 if (pmin > tt) { pmin = tt; smin = p;}
833 }
834 }
835
836 vector<Plane_t> planes(2);
837 G4ThreeVector n;
838 if (b1) {
839 n = iT.TransformAxis(n1);
840 } else {
841 n = iT.TransformAxis(n2);
842 }
843 planes[0] = { n, -limits[0]* n};
844 planes[1] = { -n, limits[1]* n};
845 vector<G4ThreeVector> vside = PhiCrossN(planes);
846
847 for (const G4ThreeVector& p : vside) {
848 // double t = n0t*(p-limits[0]);
849 double tt = n0t * p;
850 // cout<<tt<<" "<<p<<" "<<endl;
851 if (pmax < tt) { pmax = tt; smax = p;}
852 if (pmin > tt) { pmin = tt; smin = p;}
853 }
854 }
855 // maximal distance in +- directions
856 G4ThreeVector rp = maxdist(n0t), rm = maxdist(-n0t);
857 if (bb.Inside(T.TransformPoint(rm))) {
858 double tt = rm * n0t;
859 if (pmin > tt) {pmin = tt; smin = rm;}
860 }
861 if (bb.Inside(T.TransformPoint(rp))) {
862 double tt = rp * n0t;
863 if (pmax < tt) {pmax = tt; smax = rp;}
864 }
865
866 // to mother volume coordinate system
867 T.ApplyPointTransform(smin);
868 T.ApplyPointTransform(smax);
869 pmin = n0 * smin;
870 pmax = n0 * smax;
871
872 pmin -= kCarTolerance;
873 pmax += kCarTolerance;
874 // bool hit = pmin > -kInfinity && pmax < kInfinity;
875 bool hit = pmin < pmax;
876
877#if COMPARE==10
878 auto surfhit = [this, &bb, &T, &n0, &n0t](double & pmin, double & pmax, bool print = false)->bool {
879 const int N = 1000 * 1000;
880 if (fsurf.size() == 0) for (int i = 0; i < N; i++) fsurf.push_back(GetPointOnSurface());
881
882 int umin = -1, umax = -1;
883 double wmin = 1e99, wmax = -1e99;
884 for (int i = 0; i < N; i++)
885 {
886 if (bb.Inside(T.TransformPoint(fsurf[i]))) {
887 double w = n0t * fsurf[i];
888 if (wmin > w) {wmin = w; umin = i;}
889 if (wmax < w) {wmax = w; umax = i;}
890 }
891 }
892 if (print)cout << umin << " " << umax << " " << wmin << " " << wmax << endl;
893 if (umin >= 0 && umax >= 0)
894 {
895 G4ThreeVector qmin = fsurf[umin], qmax = fsurf[umax];
896 T.ApplyPointTransform(qmin);
897 T.ApplyPointTransform(qmax);
898 pmin = n0 * qmin, pmax = n0 * qmax;
899 return true;
900 }
901 return false;
902 };
903
904 bool res = fshape->CalculateExtent(A, bb, T, pMin, pMax);
905 double srfmin = kInfinity, srfmax = -srfmin;
906 bool sHit = surfhit(srfmin, srfmax);
907 double diff = kCarTolerance;
908 diff = 10;
909 // if (abs(pmin - pMin) > diff || abs(pmax - pMax) > diff || hit != res) {
910 if ((abs(pmin - srfmin) > diff || abs(pmax - srfmax) > diff) && sHit) {
911 cout << "===================================\n";
912 cout << GetName() << " " << fcache.size() << " " << fphi << " " << fdphi << " " << ftwopi << "\n";
913 cout << hit << " " << res << " " << b1 << " " << b2 << "\n";
914 if (sHit) {
915 cout << "ss " << srfmin << " " << srfmax << "\n";
916 } else {
917 cout << "ss : not in bounding box" << "\n";
918 }
919 cout << "my " << pmin << " " << pmax << "\n";
920 cout << "tc " << pMin << " " << pMax << "\n";
921 cout << "df " << pmin - pMin << " " << pmax - pMax << "\n";
922 G4ThreeVector bmin(bb.GetMinXExtent(), bb.GetMinYExtent(), bb.GetMinZExtent());
923 G4ThreeVector bmax(bb.GetMaxXExtent(), bb.GetMaxYExtent(), bb.GetMaxZExtent());
924 cout << "Axis=" << A << " " << bmin << " " << bmax << " " << T << "\n";
925 cout << rp << " " << rm << "\n";
926 cout << smin << " " << smax << "\n";
927 cout << flush;
928 // _exit(0);
929 }
930 // cout<<endl;
931#endif
932 pMin = pmin;
933 pMax = pmax;
934
935 return hit;
936}
937
938// True if (x,y) is within the shape rotation
939inline bool BelleLathe::insector(double x, double y) const
940{
941 double d0 = fn0x * x + fn0y * y;
942 double d1 = fn1x * x + fn1y * y;
943 bool b0 = d0 < 0, b1 = d1 < 0;
944 return fgtpi ? b0 || b1 : b0 && b1;
945}
946
947int BelleLathe::wn_poly(const zr_t& r) const
948{
949 int wn = 0;
950 vector<double>::const_iterator it = upper_bound(fz.begin(), fz.end(), r.z);
951 // cout<<r<<" "<<fz.size()<<" "<<it-fz.begin()<<endl;
952 if (it != fz.begin() && it != fz.end()) {
953 int k = it - fz.begin();
954 for (int i = findx[k - 1]; i != findx[k]; i++) {
955 const cachezr_t& s = fcache[fseg[i]];
956 double dz = r.z - s.z, dr = r.r - s.r;
957 double crs = s.dr * dz - s.dz * dr;
958 wn -= (crs > 0) - (crs < 0);
959 }
960 }
961 return wn;
962}
963
964double BelleLathe::mindist(const zr_t& r) const
965{
966 double d = kInfinity;
967 int i = 0, n = fcache.size();
968 do {
969 const cachezr_t& s = fcache[i];
970 double dz = r.z - s.z, dr = r.r - s.r;
971 double dot = s.dz * dz + s.dr * dr; // projection of the point on the segment
972 if (dot < 0) {
973 d = min(d, dz * dz + dr * dr); // distance to the first point of the segment
974 } else if (dot <= s.s2) { // point should be within the segment
975 double crs = s.dr * dz - s.dz * dr;
976 d = min(d, crs * crs * s.is2);
977 }
978 } while (++i < n);
979 d = sqrt(d);
980 d = (wn_poly(r) == 2) ? -d : d;
981 return d;
982}
983
984// Return whether point inside/outside/on surface, using tolerance
985EInside BelleLathe::Inside(const G4ThreeVector& p) const
986{
987 COUNTER(0);
988 const double delta = 0.5 * kCarTolerance;
989 EInside res = kInside;
990 if (!ftwopi) {
991 double d0 = fn0x * p.x() + fn0y * p.y();
992 double d1 = fn1x * p.x() + fn1y * p.y();
993 if (fgtpi) {
994 if (d0 > delta && d1 > delta) { res = kOutside;}
995 else if (d0 > -delta && d1 > -delta) { res = kSurface;}
996 } else {
997 if (d0 > delta || d1 > delta) { res = kOutside;}
998 else if (d0 > -delta || d1 > -delta) { res = kSurface;}
999 }
1000 }
1001 if (res != kOutside) {
1002 zr_t r = {p.z(), p.perp()};
1003 double d = mindist(r);
1004 if (res == kSurface && d < delta) res = kSurface;
1005 else if (d > delta) res = kOutside;
1006 else if (d > -delta) res = kSurface;
1007 else res = kInside;
1008 }
1009
1010#if COMPARE==1
1011 EInside dd = fshape->Inside(p);
1012 if (1 || dd != res) {
1013 double d0 = fn0x * p.x() + fn0y * p.y();
1014 double d1 = fn1x * p.x() + fn1y * p.y();
1015 // if (abs(d0) > kCarTolerance && abs(d1) > kCarTolerance) {
1016 int oldprec = cout.precision(16);
1017 zr_t r = {p.z(), p.perp()};
1018 cout << GetName() << " Inside(p) " << p << " " << r << " my=" << res << " tc=" << dd <<
1019 " dist=" << mindist(r) << " " << d0 << " " << d1 << endl;
1020 cout.precision(oldprec);
1021 // }
1022 }
1023#endif
1024 MATCHOUT("BelleLathe::Inside(p) " << p << " res= " << res);
1025 return res;
1026}
1027
1028zr_t BelleLathe::normal(const zr_t& r, double& d2) const
1029{
1030 double d = std::numeric_limits<double>::infinity(), t = 0;
1031 int iseg = -1;
1032 for (int i = 0, imax = fcache.size(); i < imax; i++) {
1033 const cachezr_t& s = fcache[i];
1034 double dz = r.z - s.z, dr = r.r - s.r;
1035 double dot = s.dz * dz + s.dr * dr; // projection of the point on the segment
1036 if (dot < 0) {
1037 double dist = dz * dz + dr * dr; // distance to the first point of the segment
1038 if (dist < d) { d = dist; t = dot * s.is2; iseg = i;}
1039 } else if (dot <= s.s2) { // point should be within the segment
1040 double crs = s.dr * dz - s.dz * dr;
1041 double dist = crs * crs * s.is2;
1042 if (dist < d) { d = dist; t = dot * s.is2; iseg = i;}
1043 }
1044 }
1045 d2 = d;
1046
1047 auto getn = [this](int i)->zr_t{
1048 int imax = fcache.size();
1049 int i0 = i;
1050 if (i == -1) i0 = imax;
1051 const cachezr_t& s = fcache[i0];
1052 double is = sqrt(s.is2);
1053 return {s.dr * is, -s.dz * is};
1054 };
1055 return getn(iseg);
1056
1057 if (t < 0.0) {
1058 const cachezr_t& s = fcache[iseg];
1059 zr_t dist = {r.z - s.z, r.r - s.r};
1060 double dist2 = dist.z * dist.z + dist.r * dist.r;
1061 if (dist2 > 1e-18) {
1062 double q = 1 / sqrt(dist2);
1063 if (wn_poly(r) == 2) q = -q;
1064 return {dist.z * q, dist.r * q};
1065 } else {
1066 zr_t n = getn(iseg), np = getn(iseg - 1);
1067 n.z += np.z; n.r += np.r;
1068 double n2 = n.z * n.z + n.r * n.r;
1069 double q = 1 / sqrt(n2);
1070 n.z *= q, n.r *= q;
1071 return n;
1072 }
1073 }
1074 return getn(iseg);
1075}
1076
1077// Calculate side nearest to p, and return normal
1078// If 2+ sides equidistant, first side's normal returned (arbitrarily)
1079G4ThreeVector BelleLathe::SurfaceNormal(const G4ThreeVector& p) const
1080{
1081 COUNTER(1);
1082
1083 auto side = [this](const zr_t & r, double d, int iside) {
1084 double nx = (iside) ? fn1x : fn0x, ny = (iside) ? fn1y : fn0y;
1085 if (wn_poly(r) == 2) return G4ThreeVector(nx, ny, 0);
1086 double cphi = (iside) ? fc1 : fc0, sphi = (iside) ? fs1 : fc0;
1087
1088 double d2; zr_t n = normal(r, d2);
1089 double x = cphi * n.r, y = sphi * n.r;
1090 double u = sqrt(d2);
1091 d2 += d * d;
1092 G4ThreeVector res;
1093 if (d2 > 0) {
1094 double q = 1 / sqrt(d2);
1095 double cpsi = u * q, spsi = d * q;
1096 res.set(x * cpsi - y * spsi, x * spsi + y * cpsi, n.z);
1097 }
1098 res.set(x, y, n.z);
1099 return res;
1100 };
1101
1102 G4ThreeVector res;
1103 zr_t r = {p.z(), p.perp()};
1104 double d2; zr_t n = normal(r, d2);
1105 double d = sqrt(d2);
1106 double pt = hypot(p.x(), p.y());
1107
1108 if (pt > 0) {
1109 double ir = n.r / pt;
1110 res = G4ThreeVector(ir * p.x(), ir * p.y(), n.z);
1111 } else
1112 res = G4ThreeVector(n.r, 0, n.z);
1113
1114 if (!ftwopi) {
1115 double d0 = fn0x * p.x() + fn0y * p.y();
1116 double d1 = fn1x * p.x() + fn1y * p.y();
1117 zr_t r0 = {p.z(), fc0 * p.x() + fs0 * p.y()}; // projection on plane phi
1118 zr_t r1 = {p.z(), fc1 * p.x() + fs1 * p.y()}; // projection on plane phi+dphi
1119 if (fgtpi) {
1120 if (d0 > 0 && d1 > 0) { // outside sector
1121 if (d0 < d1) {
1122 res = side(r0, d0, 0); goto exit;
1123 } else {
1124 res = side(r1, -d1, 1); goto exit;
1125 }
1126 } else {// inside sector
1127 if (wn_poly(r) == 2) { // point p inside the solid
1128 if (abs(d0) < d && abs(d0) < abs(d1)) { res = G4ThreeVector(fn0x, fn0y, 0); goto exit;}
1129 if (abs(d1) < d && abs(d1) < abs(d0)) { res = G4ThreeVector(fn1x, fn1y, 0); goto exit;}
1130 }
1131 }
1132 } else {
1133 if (d0 > 0 || d1 > 0) { // outside sector
1134 if (d0 < 0) {
1135 res = side(r1, -d1, 1); goto exit;
1136 } else {
1137 res = side(r0, d0, 0); goto exit;
1138 }
1139 } else {
1140 if (wn_poly(r) == 2) { // point p inside the solid
1141 if (abs(d0) < d && abs(d0) < abs(d1)) { res = G4ThreeVector(fn0x, fn0y, 0); goto exit;}
1142 if (abs(d1) < d && abs(d1) < abs(d0)) { res = G4ThreeVector(fn1x, fn1y, 0); goto exit;}
1143 }
1144 }
1145 }
1146 }
1147exit:
1148#if COMPARE==1
1149 G4ThreeVector dd = fshape->SurfaceNormal(p);
1150 if ((res - dd).mag() > 1e-11) {
1151 int oldprec = cout.precision(16);
1152 EInside inside = fshape->Inside(p);
1153 cout << GetName() << " SurfaceNormal(p) " << p << " " << res << " " << dd << " " << res - dd << " " << inside << endl;
1154 cout.precision(oldprec);
1155 // _exit(0);
1156 }
1157#endif
1158 MATCHOUT("BelleLathe::SurfaceNormal(p,n) " << p << " res= " << res);
1159 return res;
1160}
1161
1162// Calculate exact shortest distance to any boundary from outside
1163// This is the best fast estimation of the shortest distance to trap
1164// - Returns 0 is ThreeVector inside
1165G4double BelleLathe::DistanceToIn(const G4ThreeVector& p) const
1166{
1167 COUNTER(2);
1168 double d = 0;
1169 // int sector = 0, plane = 0;
1170 if (ftwopi) {
1171 zr_t r = {p.z(), p.perp()};
1172 d = max(mindist(r), 0.0);
1173 } else {
1174 double d0 = fn0x * p.x() + fn0y * p.y();
1175 double d1 = fn1x * p.x() + fn1y * p.y();
1176
1177 if (fgtpi) {
1178 if (d0 > 0 && d1 > 0) { // outside sector
1179 if (d0 < d1) {
1180 zr_t r = {p.z(), -fn0y * p.x() + fn0x * p.y()}; // projection on plane
1181 d = sqrt(pow(max(mindist(r), 0.0), 2) + d0 * d0);
1182 } else {
1183 zr_t r = {p.z(), fn1y * p.x() - fn1x * p.y()}; // projection on plane
1184 d = sqrt(pow(max(mindist(r), 0.0), 2) + d1 * d1);
1185 }
1186 } else {
1187 zr_t r = {p.z(), p.perp()};
1188 d = max(mindist(r), 0.0);
1189 }
1190 } else {
1191 if (d0 > 0 || d1 > 0) { // outside sector
1192 if (d0 < 0) {
1193 zr_t r = {p.z(), fn1y * p.x() - fn1x * p.y()}; // projection on plane
1194 d = sqrt(pow(max(mindist(r), 0.0), 2) + d1 * d1);
1195 } else {
1196 zr_t r = {p.z(), -fn0y * p.x() + fn0x * p.y()}; // projection on plane
1197 d = sqrt(pow(max(mindist(r), 0.0), 2) + d0 * d0);
1198 }
1199 } else {
1200 zr_t r = {p.z(), p.perp()};
1201 d = max(mindist(r), 0.0);
1202 }
1203 }
1204 }
1205#if COMPARE==1
1206 // double dd = fshape->Inside(p) == 2 ? 0.0 : fshape->DistanceToIn(p);
1207 double dd = fshape->DistanceToIn(p);
1208 // if (abs(d - dd) > kCarTolerance) {
1209 if (dd > d && abs(d - dd) > kCarTolerance) {
1210 int oldprec = cout.precision(16);
1211 EInside inside = fshape->Inside(p);
1212 zr_t r = {p.z(), p.perp()};
1213 cout << GetName() << " DistanceToIn(p) " << p << " " << r << " " << d << " " << dd << " " << d - dd << " " << inside << endl;
1214 cout.precision(oldprec);
1215 // exit(0);
1216 }
1217#endif
1218 MATCHOUT("BelleLathe::DistanceToIn(p) " << p << " res= " << d);
1219 return d;
1220}
1221
1222// Calculate exact shortest distance to any boundary from inside
1223// - Returns 0 is ThreeVector outside
1224G4double BelleLathe::DistanceToOut(const G4ThreeVector& p) const
1225{
1226 // return ref->DistanceToOut(p);
1227 COUNTER(3);
1228 zr_t r = {p.z(), p.perp()};
1229 double d = mindist(r);
1230 if (!ftwopi) {
1231 double d0 = fn0x * p.x() + fn0y * p.y();
1232 double d1 = fn1x * p.x() + fn1y * p.y();
1233 if (fgtpi) {
1234 d = max(d, min(d0, d1));
1235 } else {
1236 d = max(d, max(d0, d1));
1237 }
1238 }
1239 d = max(-d, 0.0);
1240#if COMPARE==1
1241 double dd = fshape->Inside(p) == 0 ? 0.0 : fshape->DistanceToOut(p);
1242 if (abs(d - dd) > kCarTolerance) {
1243 int oldprec = cout.precision(16);
1244 zr_t r = {p.z(), p.perp()};
1245 // cout<<r<<endl;
1246 cout << GetName() << " DistanceToOut(p) " << p << " " << r << " " << d << " " << dd << " " << d - dd << endl;
1247 cout.precision(oldprec);
1248 }
1249#endif
1250 MATCHOUT("BelleLathe::DistanceToOut(p) " << p.x() << " " << p.y() << " " << p.z() << " res= " << d);
1251 return d;
1252}
1253
1254// Calculate distance to shape from outside - return kInfinity if no intersection
1255G4double BelleLathe::DistanceToIn(const G4ThreeVector& p, const G4ThreeVector& n) const
1256{
1257 // return fshape->DistanceToIn(p, n);
1258 auto getnormal = [this, &p, &n](int i, double t) ->G4ThreeVector{
1259 const int imax = fcache.size();
1260 G4ThreeVector o;
1261 if (i < 0)
1262 {
1263 } else if (i < imax)
1264 {
1265 const cachezr_t& s = fcache[i];
1266 if (s.dz == 0.0) {
1267 o.setZ(copysign(1, s.dr));
1268 } else {
1269 double x = p.x() + n.x() * t;
1270 double y = p.y() + n.y() * t;
1271 double sth = s.dr * s.is, cth = -s.dz * s.is;
1272 double ir = cth / sqrt(x * x + y * y);
1273 o.set(x * ir, y * ir, sth);
1274 }
1275 } else if (i == imax)
1276 {
1277 o.setX(fn0x), o.setY(fn0y);
1278 } else
1279 {
1280 o.setX(fn1x), o.setY(fn1y);
1281 }
1282 return o;
1283 };
1284
1285 auto hitside = [this, &p, &n](double t, const cachezr_t& s) -> bool {
1286 double z = p.z() + n.z() * t;
1287 // cout<<t<<" "<<x<<" "<<y<<" "<<z<<endl;
1288 bool k = s.zmin < z && z <= s.zmax;
1289 if (k && !ftwopi)
1290 {
1291 double x = p.x() + n.x() * t;
1292 double y = p.y() + n.y() * t;
1293 k = k && insector(x, y);
1294 }
1295 return k;
1296 };
1297
1298 auto hitzside = [this, &p, &n](double t, const cachezr_t& s) -> bool {
1299 double x = p.x() + n.x() * t;
1300 double y = p.y() + n.y() * t;
1301 double r2 = x * x + y * y;
1302 bool k = s.r2min <= r2 && r2 < s.r2max;
1303 if (k && !ftwopi)
1304 {
1305 k = k && insector(x, y);
1306 }
1307 return k;
1308 };
1309
1310 auto hitphi0side = [this, &p, &n](double t) -> bool {
1311 double x = p.x() + n.x() * t;
1312 double y = p.y() + n.y() * t;
1313 double r = x * fc0 + y * fs0;
1314 if (r >= frmin)
1315 {
1316 double z = p.z() + n.z() * t;
1317 zr_t zr = {z, r};
1318 return wn_poly(zr) == 2;
1319 }
1320 return false;
1321 };
1322
1323 auto hitphi1side = [this, &p, &n](double t) -> bool {
1324 double x = p.x() + n.x() * t;
1325 double y = p.y() + n.y() * t;
1326 double r = x * fc1 + y * fs1;
1327 if (r >= frmin)
1328 {
1329 double z = p.z() + n.z() * t;
1330 zr_t zr = {z, r};
1331 return wn_poly(zr) == 2;
1332 }
1333 return false;
1334 };
1335
1336 double tmin = kInfinity;
1337 const int imax = fcache.size();
1338 int iseg = -1, isurface = -1;
1339 const double delta = 0.5 * kCarTolerance;
1340 double inz = 1 / n.z();
1341 double nn = dotxy(n, n), np = dotxy(n, p), pp = dotxy(p, p);
1342 double pz = p.z(), pr = sqrt(pp);
1343 for (int i = 0; i < imax; i++) { // loop over sides
1344 const cachezr_t& s = fcache[i];
1345 double dz = pz - s.z, dr = pr - s.r;
1346 double d = dz * s.dr - dr * s.dz;
1347 bool surface = false;
1348 if (abs(d * s.is) < delta) {
1349 double dot = dz * s.dz + dr * s.dr;
1350 if (dot >= 0 && dot <= s.s2) {
1351 surface = true;
1352 isurface = i;
1353 }
1354 }
1355 if (s.dz == 0.0) { // z-plane
1356 if (!surface) {
1357 double t = -dz * inz;
1358 if (0 < t && t < tmin && hitzside(t, s)) { tmin = t; iseg = i;}
1359 }
1360 } else {
1361 double A, B, R2;
1362 if (s.dr == 0.0) { // cylinder
1363 double R = s.r;
1364 R2 = R * R;
1365
1366 A = -nn;
1367 B = np;
1368 } else { // cone
1369 double taz = s.ta * dz;
1370 double R = taz + s.r;
1371 R2 = R * R;
1372
1373 double nzta = n.z() * s.ta;
1374 A = nzta * nzta - nn;
1375 B = np - nzta * R;
1376 }
1377 double C = pp - R2;
1378 double D = B * B + C * A;
1379 if (D > 0) {
1380 // double sD = sqrt(D), iA = 1 / A;
1381 // double t0 = (B + sD) * iA, t1 = (B - sD) * iA;
1382 double sD = sqrt(D), sum = B + copysign(sD, B);
1383 double t0 = -C / sum, t1 = sum / A;
1384 if (surface) { // exclude solution on surface
1385 if (abs(t0) > abs(t1)) {
1386 if (t0 > 0 && t0 < tmin && hitside(t0, s)) { tmin = t0; iseg = i;}
1387 } else {
1388 if (t1 > 0 && t1 < tmin && hitside(t1, s)) { tmin = t1; iseg = i;}
1389 }
1390 } else {
1391 if (t0 > 0 && t0 < tmin && hitside(t0, s)) { tmin = t0; iseg = i;}
1392 if (t1 > 0 && t1 < tmin && hitside(t1, s)) { tmin = t1; iseg = i;}
1393 }
1394 }
1395 }
1396 }
1397
1398 if (!ftwopi) {
1399 do { // side at phi0
1400 double vn = fn0x * n.x() + fn0y * n.y();
1401 if (vn < 0) {
1402 double d = fn0x * p.x() + fn0y * p.y();
1403 double t = -d / vn;
1404 if (hitphi0side(t)) {
1405 bool surface = std::abs(d) < delta;
1406 if (surface) {
1407 tmin = 0; iseg = imax + 0;
1408 } else {
1409 if (0 < t && t < tmin) {tmin = t; iseg = imax + 0;}
1410
1411 }
1412 }
1413 }
1414 } while (0);
1415
1416 do { // side at phi0+dphi
1417 double vn = fn1x * n.x() + fn1y * n.y();
1418 if (vn < 0) {
1419 double d = fn1x * p.x() + fn1y * p.y();
1420 double t = -d / vn;
1421 if (hitphi1side(t)) {
1422 bool surface = std::abs(d) < delta;
1423 if (surface) {
1424 tmin = 0; iseg = imax + 1;
1425 } else {
1426 if (0 < t && t < tmin) { tmin = t; iseg = imax + 1;}
1427 }
1428 }
1429 }
1430 } while (0);
1431 }
1432
1433 if (iseg >= 0) {
1434 if (getnormal(iseg, tmin)*n > 0) tmin = 0;
1435 // if (getnormal(iseg, tmin)*n > 0) tmin = kInfinity; // mimic genericpolycone
1436 }
1437
1438 auto convex = [this, imax](int i) -> bool{
1439 if (i < imax)
1440 return fcache[i].isconvex;
1441 else
1442 return !fgtpi;
1443 };
1444
1445 if (tmin >= 0 && tmin < kInfinity) {
1446 if (isurface >= 0) if (convex(isurface) && getnormal(isurface, 0)*n >= 0) tmin = kInfinity;
1447 } else {
1448 if (Inside(p) == kSurface) {
1449 if (isurface >= 0) {
1450 tmin = (getnormal(isurface, 0) * n >= 0) ? kInfinity : 0; // mimic genericpolycone
1451 }
1452 }
1453 }
1454
1455#if COMPARE==1
1456 // double dd = fshape->Inside(p) == 2 ? 0.0 : fshape->DistanceToIn(p, n);
1457 double dd = fshape->DistanceToIn(p, n);
1458 if (abs(tmin - dd) > 1e-10) {
1459 int oldprec = cout.precision(16);
1460 EInside inside = fshape->Inside(p);
1461 cout << GetName() << " DistanceToIn(p,v) " << p << " " << n << " " << tmin << " " << dd << " " << tmin - dd << " " << inside << " "
1462 << Inside(p) << " iseg = " << iseg << " " << isurface << endl;
1463 if (isurface >= 0) cout << getnormal(isurface, 0) << endl;
1464 cout.precision(oldprec);
1465 }
1466#endif
1467 tmin = max(0.0, tmin);
1468 MATCHOUT("BelleLathe::DistanceToIn(p,n) " << p << " " << n << " res= " << tmin);
1469 return tmin;
1470}
1471
1472// Calculate distance to surface of shape from inside
1473G4double BelleLathe::DistanceToOut(const G4ThreeVector& p, const G4ThreeVector& v,
1474 const G4bool calcNorm, G4bool* validNorm, G4ThreeVector* n) const
1475{
1476 // return fshape->DistanceToOut(p, v, calcNorm, validNorm, n);
1477 auto getnormal = [this, &p, &v](int i, double t)->G4ThreeVector{
1478 const int imax = fcache.size();
1479 G4ThreeVector o;
1480 if (i < 0)
1481 {
1482 } else if (i < imax)
1483 {
1484 const cachezr_t& s = fcache[i];
1485 if (s.dz == 0.0) {
1486 o.setZ(copysign(1, s.dr));
1487 } else {
1488 double x = p.x() + v.x() * t;
1489 double y = p.y() + v.y() * t;
1490 double sth = s.dr * s.is, cth = -s.dz * s.is;
1491 double ir = cth / sqrt(x * x + y * y);
1492 o.set(x * ir, y * ir, sth);
1493 }
1494 } else if (i == imax)
1495 {
1496 o.setX(fn0x), o.setY(fn0y);
1497 } else
1498 {
1499 o.setX(fn1x), o.setY(fn1y);
1500 }
1501 return o;
1502 };
1503
1504 double nn = dotxy(v, v), np = dotxy(v, p), pp = dotxy(p, p);
1505 auto hitside = [this, &p, &v, nn, np, pp](double t, const cachezr_t& s) -> bool {
1506 double z = p.z() + v.z() * t;
1507 // cout<<t<<" "<<x<<" "<<y<<" "<<z<<endl;
1508 double dot = v.z() * s.dr * sqrt(pp + ((np + np) + nn * t) * t) - s.dz * (np + nn * t);
1509 bool k = s.zmin < z && z <= s.zmax && dot > 0;
1510 if (k && !ftwopi)
1511 {
1512 double x = p.x() + v.x() * t;
1513 double y = p.y() + v.y() * t;
1514
1515 k = k && insector(x, y);
1516 }
1517 return k;
1518 };
1519
1520 auto hitzside = [this, &p, &v](double t, const cachezr_t& s) -> bool {
1521 double x = p.x() + v.x() * t;
1522 double y = p.y() + v.y() * t;
1523 double r2 = x * x + y * y;
1524 bool k = s.dr * v.z() > 0 && s.r2min <= r2 && r2 < s.r2max;
1525 if (k && !ftwopi)
1526 {
1527 k = k && insector(x, y);
1528 }
1529 return k;
1530 };
1531
1532 auto hitphi0side = [this, &p, &v](double t) -> bool {
1533 double x = p.x() + v.x() * t;
1534 double y = p.y() + v.y() * t;
1535 double r = x * fc0 + y * fs0;
1536 if (r >= frmin)
1537 {
1538 double z = p.z() + v.z() * t;
1539 zr_t zr = {z, r};
1540 return wn_poly(zr) == 2;
1541 }
1542 return false;
1543 };
1544
1545 auto hitphi1side = [this, &p, &v](double t) -> bool {
1546 double x = p.x() + v.x() * t;
1547 double y = p.y() + v.y() * t;
1548 double r = x * fc1 + y * fs1;
1549 if (r >= frmin)
1550 {
1551 double z = p.z() + v.z() * t;
1552 zr_t zr = {z, r};
1553 return wn_poly(zr) == 2;
1554 }
1555 return false;
1556 };
1557
1558 COUNTER(5);
1559 double tmin = kInfinity;
1560
1561 const int imax = fcache.size();
1562 int iseg = -1, isurface = -1;
1563
1564 const double delta = 0.5 * kCarTolerance;
1565 double inz = 1 / v.z();
1566 double pz = p.z(), pr = sqrt(pp);
1567
1568 for (int i = 0; i < imax; i++) {
1569 const cachezr_t& s = fcache[i];
1570
1571 double d = (pz - s.z) * s.dr - (pr - s.r) * s.dz;
1572 bool surface = abs(d * s.is) < delta;
1573 if (surface) isurface = i;
1574 if (s.dz == 0.0) {
1575 double t = (s.z - p.z()) * inz;
1576 if (surface) {
1577 if (hitzside(t, s)) {tmin = 0; iseg = i; break;}
1578 } else {
1579 if (0 < t && t < tmin && hitzside(t, s)) {tmin = t; iseg = i;}
1580 }
1581 } else {
1582 double A, B, R2;
1583 if (s.dr == 0.0) { // cylinder
1584 double R = s.r;
1585 R2 = R * R;
1586
1587 A = -nn;
1588 B = np;
1589 } else { // cone
1590 double taz = s.ta * (p.z() - s.z);
1591 double R = taz + s.r;
1592 R2 = R * R;
1593
1594 double nzta = v.z() * s.ta;
1595 A = nzta * nzta - nn;
1596 B = np - nzta * R;
1597 }
1598 double C = pp - R2;
1599 double D = B * B + C * A;
1600 if (D > 0) {
1601 double sD = sqrt(D);
1602 double sum = B + copysign(sD, B);
1603 double t0 = -C / sum, t1 = sum / A;
1604 // cout<<t0<<" "<<t1<<" "<<endl;
1605 if (surface) { //exclude solution on surface
1606 if (abs(t0) < abs(t1)) {
1607 if (hitside(t0, s)) { tmin = 0; iseg = i; break;}
1608 if (0 < t1 && t1 < tmin && hitside(t1, s)) { tmin = t1; iseg = i;}
1609 } else {
1610 if (hitside(t1, s)) { tmin = 0; iseg = i; break;}
1611 if (0 < t0 && t0 < tmin && hitside(t0, s)) { tmin = t0; iseg = i;}
1612 }
1613 } else { // check both solutions
1614 if (0 < t0 && t0 < tmin && hitside(t0, s)) { tmin = t0; iseg = i;}
1615 if (0 < t1 && t1 < tmin && hitside(t1, s)) { tmin = t1; iseg = i;}
1616 }
1617 }
1618 }
1619 // cout<<i<<" "<<iseg<<" "<<sqrt(d*d*s.is2)<<" "<<tmin<<" "<<s.zmin<<" "<<s.zmax<<endl;
1620 }
1621
1622 if (!ftwopi) {
1623 do { // side at phi0
1624 double vn = fn0x * v.x() + fn0y * v.y();
1625 if (vn > 0) {
1626 double d = fn0x * p.x() + fn0y * p.y();
1627 double t = -d / vn;
1628 if (hitphi0side(t)) {
1629 bool surface = std::abs(d) < delta;
1630 if (surface) {
1631 tmin = 0; iseg = imax + 0;
1632 } else {
1633 if (0 < t && t < tmin) {tmin = t; iseg = imax + 0;}
1634
1635 }
1636 }
1637 }
1638 } while (0);
1639
1640 do { // side at phi0+dphi
1641 double vn = fn1x * v.x() + fn1y * v.y();
1642 if (vn > 0) {
1643 double d = fn1x * p.x() + fn1y * p.y();
1644 double t = -d / vn;
1645 if (hitphi1side(t)) {
1646 bool surface = std::abs(d) < delta;
1647 if (surface) {
1648 tmin = 0; iseg = imax + 1;
1649 } else {
1650 if (0 < t && t < tmin) { tmin = t; iseg = imax + 1;}
1651 }
1652 }
1653 }
1654 } while (0);
1655 }
1656
1657 auto convex = [this, imax](int i) -> bool{
1658 if (i < imax)
1659 return fcache[i].isconvex;
1660 else
1661 return !fgtpi;
1662 };
1663
1664 if (calcNorm) {
1665 if (tmin >= 0 && tmin < kInfinity) {
1666 *n = getnormal(iseg, tmin);
1667 *validNorm = convex(iseg);
1668 } else {
1669 if (Inside(p) == kSurface) {
1670 if (isurface >= 0) {
1671 *n = getnormal(isurface, tmin);
1672 *validNorm = convex(isurface);
1673 tmin = 0;
1674 }
1675 } else {
1676 *validNorm = false;
1677 }
1678 }
1679 }
1680 // cout<<"tmin "<<tmin<<endl;
1681#if COMPARE==1
1682 do {
1683 // G4ThreeVector p0(1210.555046, -292.9578965, -36.71671492);
1684 // if ((p - p0).mag() > 1e-2)continue;
1685 bool isvalid;
1686 G4ThreeVector norm;
1687 double dd = fshape->DistanceToOut(p, v, calcNorm, &isvalid, &norm);
1688 if (abs(tmin - dd) > 1e-10 || (calcNorm && *validNorm != isvalid)) {
1689 int oldprec = cout.precision(16);
1690 cout << GetName() << " DistanceToOut(p,v) p,v =" << curl_t(p) << curl_t(v) << " calcNorm=" << calcNorm
1691 << " myInside=" << Inside(p) << " tmin=" << tmin << " dd=" << dd << " d=" << tmin - dd << " iseg=" << iseg << " isurf=" << isurface
1692 << " ";
1693 if (calcNorm) cout << "myIsValid = " << *validNorm << " tIsValid=" << isvalid << " myn=" << (*n) << " tn=" << (norm);
1694 cout << endl;
1695 cout.precision(oldprec);
1696 // _exit(0);
1697 }
1698 } while (0);
1699#endif
1700 MATCHOUT("BelleLathe::DistanceToOut(p,v) " << p << " " << v << " res= " << tmin);
1701 return tmin;
1702}
1703
1705{
1706 ftlist.clear();
1707 unsigned int n = fcontour.size();
1708 vector<int> indx;
1709 for (unsigned int i = 0; i < n; i++) indx.push_back(i);
1710 int count = 0;
1711 while (indx.size() > 3 && ++count < 200) {
1712 unsigned int ni = indx.size();
1713 for (unsigned int i = 0; i < ni; i++) {
1714 int i0 = indx[i], i1 = indx[(i + 1) % ni], i2 = indx[(i + 2) % ni];
1715 double nx = fcontour[i2].z - fcontour[i0].z;
1716 double ny = fcontour[i2].r - fcontour[i0].r;
1717 double d1 = nx * (fcontour[i1].r - fcontour[i0].r) - ny * (fcontour[i1].z - fcontour[i0].z);
1718 bool ear = true;
1719 for (unsigned int j = 0; j < ni - 3; j++) {
1720 int k = indx[(i + 3 + j) % ni];
1721 double d = nx * (fcontour[k].r - fcontour[i0].r) - ny * (fcontour[k].z - fcontour[i0].z);
1722 if (d * d1 > 0) {
1723 ear = false;
1724 break;
1725 }
1726 }
1727 if (ear) {
1728 triangle_t t = {i0, i1, i2};
1729 ftlist.push_back(t);
1730 indx.erase(indx.begin() + (i + 1) % ni);
1731 break;
1732 }
1733 }
1734 }
1735 if (indx.size() == 3) {
1736 triangle_t t = {indx[0], indx[1], indx[2]};
1737 ftlist.push_back(t);
1738 }
1739}
1740
1742{
1743 double vol = 0;
1744 for (const cachezr_t& s : fcache) vol += s.dz * ((3 * s.r) * (s.r + s.dr) + s.dr * s.dr);
1745 fCubicVolume = -fdphi * vol / 6;
1746
1747 double totalarea = 0;
1748 if (!ftwopi) {
1749 eartrim();
1750 for (const triangle_t& t : ftlist) {
1751 const zr_t& p0 = fcontour[t.i0], &p1 = fcontour[t.i1], &p2 = fcontour[t.i2];
1752 double area = (p1.z - p0.z) * (p2.r - p0.r) - (p1.r - p0.r) * (p2.z - p0.z);
1753 totalarea += abs(area);
1754 farea.push_back(totalarea);
1755 }
1756 }
1757
1758 for (const cachezr_t& s : fcache) {
1759 double area = fdphi * (s.r + 0.5 * s.dr) * sqrt(s.dz * s.dz + s.dr * s.dr);
1760 totalarea += area;
1761 farea.push_back(totalarea);
1762 }
1763 fSurfaceArea = farea.back();
1764}
1765
1767{
1768 auto GetPointOnTriangle = [this](const triangle_t& t)-> G4ThreeVector{
1769 // barycentric coordinates
1770 double a1 = CLHEP::RandFlat::shoot(0., 1.), a2 = CLHEP::RandFlat::shoot(0., 1.);
1771 if (a1 + a2 > 1) { a1 = 1 - a1; a2 = 1 - a2;}
1772 double a0 = 1 - (a1 + a2);
1773 const zr_t& p0 = fcontour[t.i0], &p1 = fcontour[t.i1], &p2 = fcontour[t.i2];
1774 zr_t p = {p0.z* a0 + p1.z* a1 + p2.z * a2, p0.r* a0 + p1.r* a1 + p2.r * a2};
1775 double c, s;
1776 if (CLHEP::RandFlat::shoot(0., 1.) > 0.5) // select phi side
1777 {
1778 c = -fn0y; s = fn0x;
1779 } else
1780 {
1781 c = fn1y; s = -fn1x;
1782 }
1783 G4ThreeVector r1(p.r * c, p.r * s, p.z);
1784 return r1;
1785 };
1786
1787 double rnd = CLHEP::RandFlat::shoot(0., farea.back());
1788 std::vector<double>::const_iterator it = std::lower_bound(farea.begin(), farea.end(), rnd);
1789 unsigned int i = it - farea.begin();
1790
1791 if (!ftwopi) {
1792 if (i < ftlist.size()) {
1793 return GetPointOnTriangle(ftlist[i]);
1794 } else {
1795 i -= ftlist.size();
1796 }
1797 }
1798
1799 const cachezr_t& s = fcache[i];
1800 double I = 2 * s.r + s.dr;
1801 double Iw = CLHEP::RandFlat::shoot(0., I);
1802 double q = sqrt(Iw * s.dr + s.r * s.r);
1803 double t = Iw / (q + s.r);
1804 double z = s.z + s.dz * t;
1805 double r = s.r + s.dr * t;
1806 double phi = CLHEP::RandFlat::shoot(fphi, fphi + fdphi);
1807 double x = r * cos(phi), y = r * sin(phi);
1808 return G4ThreeVector(x, y, z);
1809}
1810
1811// GetEntityType
1812G4GeometryType BelleLathe::GetEntityType() const
1813{
1814 return G4String("BelleLathe");
1815}
1816
1817// Make a clone of the object
1818G4VSolid* BelleLathe::Clone() const
1819{
1820 return new BelleLathe(*this);
1821}
1822
1823// Stream object contents to an output stream
1824std::ostream& BelleLathe::StreamInfo(std::ostream& os) const
1825{
1826 G4int oldprc = os.precision(16);
1827 os << "-----------------------------------------------------------\n"
1828 << " *** Dump for solid - " << GetName() << " ***\n"
1829 << " ===================================================\n"
1830 << " Solid type: BelleLathe\n"
1831 << " Contour: " << fcontour.size() << " sides, {z, r} points \n";
1832 for (int i = 0, imax = fcontour.size(); i < imax; i++) {
1833 os << fcontour[i] << ", ";
1834 }
1835 os << "\n";
1836 for (int i = 0, imax = fcontour.size(); i < imax; i++) {
1837 os << fcache[i].isconvex << ", ";
1838 }
1839 os << "\n";
1840 os << "phi0 = " << fphi << ", dphi = " << fdphi << ", Full Circle = " << (ftwopi ? "yes" : "no") << "\n";
1841 double xmin = fzmin - 0.05 * (fzmax - fzmin), xmax = fzmax + 0.05 * (fzmax - fzmin);
1842 double ymin = frmin - 0.05 * (frmax - frmin), ymax = frmax + 0.05 * (frmax - frmin);
1843 os << " BB: " << xmin << ", " << xmax << ", " << ymin << ", " << ymax << endl;
1844 os << "-----------------------------------------------------------\n";
1845 os.precision(oldprc);
1846
1847 return os;
1848}
1849
1850// Methods for visualisation
1851void BelleLathe::DescribeYourselfTo(G4VGraphicsScene& scene) const
1852{
1853 scene.AddSolid(*this);
1854}
1855
1856// Function to define the bounding box
1857void BelleLathe::BoundingLimits(G4ThreeVector& pMin, G4ThreeVector& pMax) const
1858{
1859 std::vector<vector_t> points;
1860 vector_t point{};
1861
1862 // Placeholder vectors
1863 const double inf = std::numeric_limits<double>::infinity();
1864 G4ThreeVector minimum(inf, inf, inf), maximum(-inf, -inf, -inf);
1865
1866 // Outer vertices
1867 if (fdphi < 2 * M_PI) { // Only axis-crossings are relevant if the shape is a full circle
1868 point.x = frmax * cos(fphi); point.y = frmax * sin(fphi);
1869 points.push_back(point);
1870 point.x = frmax * cos(fphi + fdphi); point.y = frmax * sin(fphi + fdphi);
1871 points.push_back(point);
1872 }
1873
1874 // Inner vertices
1875 point.x = frmin * cos(fphi); point.y = frmin * sin(fphi);
1876 points.push_back(point);
1877 if (frmin != 0) { // Avoid duplicate (0,0)
1878 point.x = frmin * cos(fphi + fdphi); point.y = frmin * sin(fphi + fdphi);
1879 points.push_back(point);
1880 }
1881
1882 // Check if shape crosses an axis
1883 // Because the inside is concave, extremums will always be at vertices
1884 // Because the outside is convex, extremums may be at an axis-crossing
1885 if (insector(0, 1)) {
1886 point.x = 0; point.y = frmax;
1887 points.push_back(point);
1888 }
1889 if (insector(-1, 0)) {
1890 point.x = -frmax; point.y = 0;
1891 points.push_back(point);
1892 }
1893 if (insector(0, -1)) {
1894 point.x = 0; point.y = -frmax;
1895 points.push_back(point);
1896 }
1897 if (insector(1, 0)) {
1898 point.x = frmax; point.y = 0;
1899 points.push_back(point);
1900 }
1901
1902 for (std::vector<vector_t>::size_type i = 0; i != points.size(); i++) {
1903 // global min in x
1904 if (points[i].x < minimum.x())
1905 minimum.setX(points[i].x);
1906
1907 // global min in y
1908 if (points[i].y < minimum.y())
1909 minimum.setY(points[i].y);
1910
1911 // global max in x
1912 if (points[i].x > maximum.x())
1913 maximum.setX(points[i].x);
1914
1915 // global max in y
1916 if (points[i].y > maximum.y())
1917 maximum.setY(points[i].y);
1918 }
1919
1920 // Set z extremum values
1921 minimum.setZ(fzmin);
1922 maximum.setZ(fzmax);
1923
1924 // Assign
1925 pMin = minimum;
1926 pMax = maximum;
1927}
1928
1929void takePolyhedron(const HepPolyhedron& p)
1930{
1931 int i, nnode, iNodes[5], iVis[4], iFaces[4];
1932
1933 for (int iface = 1; iface <= p.GetNoFacets(); iface++) {
1934 p.GetFacet(iface, nnode, iNodes, iVis, iFaces);
1935 for (i = 0; i < nnode; i++) {
1936 if (iNodes[i] < 1 || iNodes[i] > p.GetNoVertices()) { //G.Barrand
1937 // processor_error = 1;
1938 G4cerr
1939 << "BooleanProcessor::takePolyhedron : problem 1."
1940 << G4endl;
1941 }
1942 if (iFaces[i] < 1 || iFaces[i] > p.GetNoFacets()) { //G.Barrand
1943 // processor_error = 1;
1944 G4cerr
1945 << "BooleanProcessor::takePolyhedron : problem 2. "
1946 << i << " " << iFaces[i] << " " << p.GetNoFacets() << G4endl;
1947 }
1948 }
1949 }
1950}
1951
1952PolyhedronBelleLathe::PolyhedronBelleLathe(const std::vector<zr_t>& v, const std::vector<triangle_t>& t, double phi, double dphi)
1953{
1954 int nphi = GetNumberOfRotationSteps();
1955 bool twopi = abs(dphi - 2 * M_PI) < 1e-6;
1956 int n = v.size();
1957 if (twopi) {
1958 int nv = n * nphi;
1959 int nf = nv;
1960 AllocateMemory(nv, nf);
1961
1962 auto vnum = [nphi, n](int iphi, int ip) {
1963 return (iphi % nphi) * n + (ip % n) + 1;
1964 };
1965
1966 int fcount = 1;
1967 double dfi = dphi / nphi;
1968 for (int i = 0; i < nphi; i++) {
1969 double fi = phi + i * dfi;
1970 double cf = cos(fi), sf = sin(fi);
1971 for (int j = 0; j < n; j++) pV[vnum(i, j)].set(v[j].r * cf, v[j].r * sf, v[j].z);
1972 for (int j = 0; j < n; j++) pF[fcount++ ] = G4Facet(vnum(i, j), 0, vnum(i, j + 1), 0, vnum(i + 1, j + 1), 0, vnum(i + 1, j), 0);
1973 }
1974 } else {
1975 // cout<<"NPHI = "<<nphi<<" "<<phi<<" "<<dphi<<endl;
1976 nphi = int(nphi * (dphi / (2 * M_PI)) + 0.5);
1977 nphi = nphi > 3 ? nphi : 3;
1978
1979 // cout<<"NPHI = "<<nphi<<endl;
1980
1981 int nv = n * nphi;
1982 int nf = n * (nphi - 1) + 2 * t.size();
1983 AllocateMemory(nv, nf);
1984
1985 auto vnum = [n](int iphi, int ip) {
1986 return iphi * n + (ip % n) + 1;
1987 };
1988
1989 int fcount = 1;
1990 double dfi = dphi / (nphi - 1);
1991 for (int i = 0; i < nphi; i++) {
1992 double fi = phi + i * dfi;
1993 double cf = cos(fi), sf = sin(fi);
1994 for (int j = 0; j < n; j++) pV[vnum(i, j)].set(v[j].r * cf, v[j].r * sf, v[j].z);
1995 if (i == nphi - 1) break;
1996 for (int j = 0; j < n; j++) pF[fcount++] = G4Facet(vnum(i, j), 0, vnum(i, j + 1), 0, vnum(i + 1, j + 1), 0, vnum(i + 1, j), 0);
1997 }
1998
1999 for (const triangle_t& k : t) pF[fcount++] = G4Facet(vnum(0, k.i0), 0, vnum(0, k.i2), 0, vnum(0, k.i1), 0, 0, 0);
2000 int i = nphi - 1;
2001 for (const triangle_t& k : t) pF[fcount++] = G4Facet(vnum(i, k.i0), 0, vnum(i, k.i1), 0, vnum(i, k.i2), 0, 0, 0);
2002
2003 }
2004 SetReferences();
2005 // takePolyhedron(*this);
2006}
2007
2009
2010G4Polyhedron* BelleLathe::CreatePolyhedron() const
2011{
2012 eartrim();
2014}
2015
2016#if 0
2017#include <immintrin.h>
2018double mindistsimd(const zr_t& r, const vector<cachezr_t>& contour)
2019{
2020 double d = kInfinity;
2021 int i = 0, n = contour.size();
2022 __m128d zero = _mm_set_sd(0);
2023 __m128d one = _mm_set_sd(1);
2024 double wn = 0;
2025 do {
2026 const cachezr_t& s = contour[i];
2027 double dz = r.z - s.z, dr = r.r - s.r;
2028 double crs = s.dr * dz - s.dz * dr;
2029 double dot = s.dz * dz + s.dr * dr; // projection of the point on the segment
2030 // if(s.zmin<=r.z&&r.z<s.zmax) wn -= (crs>0) - (crs<0);
2031 __m128d crssd = _mm_set_sd(crs);
2032 __m128d maskgt = _mm_cmpgt_sd(crssd, zero);
2033 __m128d masklt = _mm_cmplt_sd(crssd, zero);
2034 __m128d left = _mm_sub_sd(_mm_and_pd(maskgt, one), _mm_and_pd(masklt, one));
2035 __m128d z = _mm_set_sd(s.z);
2036 __m128d mask = _mm_and_pd(_mm_cmple_sd(_mm_set_sd(s.zmin), z), _mm_cmplt_sd(z, _mm_set_sd(s.zmax)));
2037 left = _mm_and_pd(mask, left);
2038 double du = dz * dz + dr * dr;
2039 double dv = crs * crs * s.is2;
2040
2041 masklt = _mm_cmplt_sd(_mm_set_sd(dot), zero);
2042 maskgt = _mm_cmpgt_sd(_mm_set_sd(dot), _mm_set_sd(s.s2));
2043
2044 __m128d uu = _mm_or_pd(_mm_and_pd(maskgt, _mm_set_sd(kInfinity)), _mm_andnot_pd(maskgt, _mm_set_sd(dv)));
2045 __m128d vv = _mm_or_pd(_mm_and_pd(masklt, _mm_set_sd(min(d, du))), _mm_andnot_pd(masklt, _mm_set_sd(min(d, uu[0]))));
2046 wn -= left[0];
2047 d = vv[0];
2048 } while (++i < n);
2049 d = sqrt(d);
2050 d = (wn == 2) ? -d : d;
2051 // cout<<wn<<" "<<d<<endl;
2052 // cout<<sqrt(dp)<<" "<<sqrt(dm)<<endl;
2053 return d;
2054}
2055inline int left(const zr_t& r0, const zr_t& r1, const zr_t& r)
2056{
2057 double d = (r1.z - r0.z) * (r.r - r0.r) - (r.z - r0.z) * (r1.r - r0.r);
2058 return (d > 0) - (d < 0);
2059}
2060
2061inline int checkside(const zr_t& s0, const zr_t& s1, const zr_t& r)
2062{
2063 double zmin = min(s0.z, s1.z), zmax = max(s0.z, s1.z);
2064 if (zmin <= r.z && r.z < zmax) return left(s0, s1, r);
2065 return 0;
2066}
2067
2068int wn_poly(const zr_t& r, const vector<zr_t>& contour)
2069{
2070 int wn = 0; // the winding number counter
2071 int i = 0, n = contour.size() - 1;
2072 do {
2073 wn += checkside(contour[i], contour[i + 1], r);
2074 } while (++i < n);
2075 wn += checkside(contour[n], contour[0], r);
2076 return wn;
2077}
2078
2079double mindist(const zr_t& r, const vector<zr_t>& contour)
2080{
2081 int wn = 0;
2082 double d = kInfinity;
2083 auto dist = [&contour, &d, &r, &wn](int i0, int i1)->void {
2084 const zr_t& s0 = contour[i0], &s1 = contour[i1];
2085 double zmin = min(s0.z, s1.z), zmax = max(s0.z, s1.z);
2086 double sz = s1.z - s0.z, sr = s1.r - s0.r;
2087 double dz = r.z - s0.z, dr = r.r - s0.r;
2088 double crs = dz * sr - sz * dr;
2089 if (zmin <= r.z && r.z < zmax) wn -= (crs > 0) - (crs < 0);
2090 double dot = sz * dz + sr * dr; // projection of the point on the segment
2091 double s2 = sz * sz + sr * sr;
2092 if (dot > s2) return; // point should be within the segment
2093 if (dot < 0)
2094 {
2095 double d2 = dz * dz + dr * dr; // distance to the first point of the segment
2096 d = min(d, d2);
2097 } else
2098 {
2099 d = min(d, crs * crs / s2);
2100 }
2101 // cout<<i0<<" "<<s0.z<<" "<<s0.r<<" "<<d<<" "<<wn<<endl;
2102 };
2103 int i = 0, n = contour.size() - 1;
2104 do {dist(i, i + 1);} while (++i < n);
2105 dist(n, 0);
2106 d = sqrt(d);
2107 d = (wn == 2) ? -d : d;
2108 // cout<<wn<<" "<<d<<endl;
2109 // cout<<sqrt(dp)<<" "<<sqrt(dm)<<endl;
2110 return d;
2111}
2112
2113double mindist(const zr_t& r, const vector<cachezr_t>& contour)
2114{
2115 double d = kInfinity;
2116 int wn = 0, i = 0, n = contour.size();
2117 do {
2118 const cachezr_t& s = contour[i];
2119 double dz = r.z - s.z, dr = r.r - s.r;
2120 double crs = s.dr * dz - s.dz * dr;
2121 double dot = s.dz * dz + s.dr * dr; // projection of the point on the segment
2122 if (s.zmin <= r.z && r.z < s.zmax) wn -= (crs > 0) - (crs < 0);
2123 if (dot > s.s2) continue; // point should be within the segment
2124 if (dot < 0) {
2125 d = min(d, dz * dz + dr * dr); // distance to the first point of the segment
2126 } else {
2127 d = min(d, crs * crs * s.is2);
2128 }
2129 // cout<<i<<" "<<s.z<<" "<<s.r<<" "<<d<<" "<<wn<<endl;
2130 // cout<<i<<" "<<d<<" "<<wn<<endl;
2131 } while (++i < n);
2132 d = sqrt(d);
2133 d = (wn == 2) ? -d : d;
2134 // cout<<wn<<" "<<d<<endl;
2135 return d;
2136}
2137
2138
2139#endif
R E
internal precision of FFTW codelets
double R
typedef autogenerated by FFTW
double fzmax
maximal z value
Definition BelleLathe.h:192
void getvolarea()
get volume area
double fphi
starting angle
Definition BelleLathe.h:179
std::vector< double > fz
vector of z values
Definition BelleLathe.h:173
G4GeometryType GetEntityType() const
Get entity type.
std::vector< int > findx
vector of indices
Definition BelleLathe.h:174
std::vector< triangle_t > ftlist
vector of triangle structs
Definition BelleLathe.h:177
void DescribeYourselfTo(G4VGraphicsScene &scene) const
Visualisation function.
void Init(const std::vector< zr_t > &, double, double)
initialize
bool ftwopi
bound within +- 2pi?
Definition BelleLathe.h:194
std::vector< double > farea
vector of area values
Definition BelleLathe.h:176
G4Polyhedron * CreatePolyhedron() const
create polyhedron
std::vector< int > fseg
vector of segments
Definition BelleLathe.h:175
G4VSolid * fshape
shape
Definition BelleLathe.h:196
double fdphi
finishing angle
Definition BelleLathe.h:180
std::vector< double > linecross(const G4ThreeVector &, const G4ThreeVector &) const
calculate all ray solid's surface intersection return ordered vector
BelleLathe & operator=(const BelleLathe &rhs)
assignment operator
void BoundingLimits(G4ThreeVector &pMin, G4ThreeVector &pMax) const
Two vectors define an axis-parallel bounding box for the shape.
virtual ~BelleLathe()
Destructor.
std::vector< zr_t > fcontour
vector of zr structs
Definition BelleLathe.h:171
bool fgtpi
greater than pi?
Definition BelleLathe.h:193
std::vector< cachezr_t > fcache
vector of cached zr structs
Definition BelleLathe.h:172
double mindist(const zr_t &) const
minimal distance
BelleLathe(const G4String &pName)
Constructor for "nominal" BelleLathe whose parameters are to be set by a G4VPVParamaterisation later.
double fzmin
minimal z value
Definition BelleLathe.h:191
zr_t normal(const zr_t &, double &) const
return normal
bool insector(double, double) const
True if (x,y) is within the shape rotation.
G4ThreeVector GetPointOnSurface() const
Get point on surface.
double frmax
maximal r value
Definition BelleLathe.h:190
G4bool CalculateExtent(const EAxis pAxis, const G4VoxelLimits &pVoxelLimit, const G4AffineTransform &pTransform, G4double &pMin, G4double &pMax) const
calculate the extent of the volume
G4ThreeVector SurfaceNormal(const G4ThreeVector &p) const
Calculate side nearest to p, and return normal.
G4double DistanceToIn(const G4ThreeVector &p, const G4ThreeVector &v) const
Calculate distance to shape from outside - return kInfinity if no intersection.
double frmin
minimal r value
Definition BelleLathe.h:189
void eartrim() const
ear trim
std::vector< G4ThreeVector > fsurf
vector of surfaces
Definition BelleLathe.h:197
G4VSolid * Clone() const
Make a clone of the object.
std::ostream & StreamInfo(std::ostream &os) const
Stream object contents to an output stream.
int wn_poly(const zr_t &) const
wn_poly
EInside Inside(const G4ThreeVector &p) const
Return whether point inside/outside/on surface, using tolerance.
void ComputeDimensions(G4VPVParameterisation *p, const G4int n, const G4VPhysicalVolume *pRep)
compute the dimensions
G4double DistanceToOut(const G4ThreeVector &p, const G4ThreeVector &v, const G4bool calcNorm=false, G4bool *validNorm=0, G4ThreeVector *n=0) const
Calculate distance to surface of shape from inside.
Belle lathe polyhedron.
Definition BelleLathe.h:201
virtual ~PolyhedronBelleLathe()
destructor
PolyhedronBelleLathe(const std::vector< zr_t > &, const std::vector< triangle_t > &, double, double)
constructor
double sqrt(double a)
sqrt for double
Definition beamHelpers.h:28
T dot(GeneralVector< T > a, GeneralVector< T > b)
dot product of two general vectors
TString rn()
Get random string.
Definition tools.h:38
Abstract base class for different kinds of events.
STL namespace.
struct for plane
cached z-r struct
Definition BelleLathe.h:31
double dz
difference in z
Definition BelleLathe.h:34
double r
r coordinate
Definition BelleLathe.h:33
double zmin
minimal z value
Definition BelleLathe.h:39
double z
z coordinate
Definition BelleLathe.h:32
double ta
ratio of dr over dz
Definition BelleLathe.h:43
double zmax
maximal z value
Definition BelleLathe.h:40
struct for a triangle
Definition BelleLathe.h:55
struct for a three vector
Definition BelleLathe.h:48
simple struct with z and r coordinates
Definition BelleLathe.h:25
double r
r coordinate
Definition BelleLathe.h:27
double z
z coordinate
Definition BelleLathe.h:26
G4ThreeVector n
Normal unit vector (x,y,z)
Definition BelleLathe.cc:42
double d
offset (d)
Definition BelleLathe.cc:43
curl struct
Definition BelleLathe.cc:67
curl_t(const G4ThreeVector &_v)
constructor
Definition BelleLathe.cc:70
G4ThreeVector v
vector
Definition BelleLathe.cc:68
solution struct
double t
t
double s
s