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