EGSnrc C++ class library  Report PIRS-898 (2021)
Iwan Kawrakow, Ernesto Mainegra-Hing, Frederic Tessier, Reid Townson and Blake Walters
egs_roundrect_cylinders.h
Go to the documentation of this file.
1 /*
2 ###############################################################################
3 #
4 # EGSnrc egs++ roundrect cylinders geometry headers
5 # Copyright (C) 2015 National Research Council Canada
6 #
7 # This file is part of EGSnrc.
8 #
9 # EGSnrc is free software: you can redistribute it and/or modify it under
10 # the terms of the GNU Affero General Public License as published by the
11 # Free Software Foundation, either version 3 of the License, or (at your
12 # option) any later version.
13 #
14 # EGSnrc is distributed in the hope that it will be useful, but WITHOUT ANY
15 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
17 # more details.
18 #
19 # You should have received a copy of the GNU Affero General Public License
20 # along with EGSnrc. If not, see <http://www.gnu.org/licenses/>.
21 #
22 ###############################################################################
23 #
24 # Author: Manuel Stoeckl, 2016
25 #
26 # Contributors: Frederic Tessier
27 # Hubert Ho
28 # Reid Townson
29 #
30 ###############################################################################
31 #
32 # A set of concentric rounded rectangle cylinders.
33 #
34 ###############################################################################
35 */
36 
37 
43 #ifndef EGS_CYLINDERS_
44 #define EGS_CYLINDERS_
45 
46 #include "egs_base_geometry.h"
47 #include "egs_math.h"
48 #include "egs_functions.h"
49 #include "egs_projectors.h"
50 
51 #include <vector>
52 using namespace std;
53 
54 #ifdef WIN32
55 
56  #ifdef BUILD_ROUNDRECT_CYLINDERS_DLL
57  #define EGS_ROUNDRECT_CYLINDERS_EXPORT __declspec(dllexport)
58  #else
59  #define EGS_ROUNDRECT_CYLINDERS_EXPORT __declspec(dllimport)
60  #endif
61  #define EGS_ROUNDRECT_CYLINDERS_LOCAL
62 
63 #else
64 
65  #ifdef HAVE_VISIBILITY
66  #define EGS_ROUNDRECT_CYLINDERS_EXPORT __attribute__ ((visibility ("default")))
67  #define EGS_ROUNDRECT_CYLINDERS_LOCAL __attribute__ ((visibility ("hidden")))
68  #else
69  #define EGS_ROUNDRECT_CYLINDERS_EXPORT
70  #define EGS_ROUNDRECT_CYLINDERS_LOCAL
71  #endif
72 
73 #endif
74 
160 template <class Tx, class Ty>
161 class EGS_ROUNDRECT_CYLINDERS_EXPORT EGS_RoundRectCylindersT :
162  public EGS_BaseGeometry {
163 
164 protected:
165 
166  EGS_Float *ax,
167  *ay,
168  *ar;
170  Tx Ax;
171  Ty Ay;
172  string mytype;
173 
174 public:
175 
180  if (nreg > 0) {
181  delete [] ax;
182  delete [] ay;
183  delete [] ar;
184  }
185  };
186 
199  EGS_RoundRectCylindersT(const vector<EGS_Float> &x_wid,
200  const vector<EGS_Float> &y_wid,
201  const vector<EGS_Float> &rad,
202  const EGS_Vector &position, const string &Name,
203  const Tx &A_x, const Ty &A_y) : EGS_BaseGeometry(Name),
204  xo(position), Ax(A_x), Ay(A_y) {
205  int nc = rad.size();
206  if (nc>0) {
207  ax = new EGS_Float [nc];
208  ay = new EGS_Float [nc];
209  ar = new EGS_Float [nc];
210  for (int i=0; i<nc; i++) {
211  ax[i] = x_wid[i];
212  ay[i] = y_wid[i];
213  ar[i] = rad[i];
214  }
215  nreg=nc;
216  }
217  mytype = Ax.getType() + Ay.getType();
218  }
219 
220  bool isInside(const EGS_Vector &src) {
221  return inRing(nreg-1, src);
222  };
223 
224  int isWhere(const EGS_Vector &x) {
225  if (!isInside(x)) {
226  return -1;
227  }
228  for (int j=0; j<nreg; j++) {
229  if (inRing(j, x)) {
230  return j;
231  }
232  }
233  return nreg-1;
234  };
235 
236  int inside(const EGS_Vector &x) {
237  return isWhere(x);
238  };
239 
240  int howfar(int ireg, const EGS_Vector &x, const EGS_Vector &u,
241  EGS_Float &t, int *newmed = 0, EGS_Vector *normal = 0) {
242  // This _can_ be constructed using just hownear
243  // and inside, but that's slow (using golden section search & smoothness hacks)
244 
245  EGS_Vector xp(x-xo);
246  // (px,py),(qx,qy) is point + vector in projected space
247  EGS_Float px = Ax*xp;
248  EGS_Float py = Ay*xp;
249  EGS_Float qx = Ax*u;
250  EGS_Float qy = Ay*u;
251 
252  if (qx == 0.0 && qy == 0.0) {
253  // perpendicular: distance not capped. No intersection
254  // so newmed, normal need not be set
255  return ireg;
256  }
257 
258  // Step 1: Transform things so that p is in the first quadrant
259  int xflip = px < 0 ? -1 : 1;
260  int yflip = py < 0 ? -1 : 1;
261  px *= xflip;
262  py *= yflip;
263  qx *= xflip;
264  qy *= yflip;
265 
266  EGS_Float dist = -1;
267  EGS_Float norm_x = 0.;
268  EGS_Float norm_y = 0.;
269 
270  // Step 2: Test for an inside collision if applicable
271  int inew = ireg;
272  bool haveInternal = false;
273  if ((ireg < 0 || ireg > 0)) {
274  // Starting outside.
275  EGS_Float ux,uy;
276  int n = ireg < 0 ? nreg - 1 : ireg - 1;
277  bool found = findLineIntersection(n, px, py, qx,qy,
278  norm_x, norm_y, ux, uy);
279  if (found) {
280  bool forward = (qx * (ux-px) + qy * (uy - py) > 0) || (px == ux && py == uy);
281  if (forward) {
282  inew = n;
283  dist = sqrt((ux-px)*(ux-px) + (uy-py)*(uy-py));
284  haveInternal = true;
285  }
286  }
287  }
288 
289  // Step 3: Test for an outside collision if applicable
290  if (ireg >= 0 && !haveInternal) {
291  // To find interior intersections, extend ray and look back
292  // -- any line from the inside ought to have 1 intersection.
293  EGS_Float sc = 1 / sqrt(qx*qx+qy*qy);
294  // Upper bound on interior diameter
295  EGS_Float skip = 2 * (ax[ireg] + ay[ireg]);
296  EGS_Float bx = px + qx * skip * sc;
297  EGS_Float by = py + qy * skip * sc;
298  EGS_Float xeflip = bx < 0 ? -1 : 1;
299  EGS_Float yeflip = by < 0 ? -1 : 1;
300  bx *= xeflip;
301  by *= yeflip;
302  EGS_Float cx = -qx*xeflip;
303  EGS_Float cy = -qy*yeflip;
304  EGS_Float ux=0.,uy=0.;
305  bool found = findLineIntersection(ireg, bx, by, cx, cy,
306  norm_x, norm_y, ux, uy);
307  if (found) {
308  norm_x *= -xeflip;
309  norm_y *= -yeflip;
310  ux *= xeflip;
311  uy *= yeflip;
312 
313  dist = sqrt((ux-px)*(ux-px) + (uy-py)*(uy-py));
314  inew = (nreg == ireg + 1) ? -1 : ireg+1;
315  }
316  else {
317  // failure occurs occasionally due to rounding error
318  }
319  }
320 
321  // Set distance, new medium, normal vector (if not null), and return inew
322 
323  // Apply changes if beam is close enough
324  if (dist < 0) {
325  // Stay in current region (out of dist)
326  return ireg;
327  }
328 
329  // Modify dist to take into account unprojected component...
330  dist *= sqrt(u.length2() / (qx*qx + qy*qy));
331  if (dist <= t) {
332  // Add a tiny amount to counteract backward rounding drift
333  t = dist + boundaryTolerance;
334  if (newmed) {
335  *newmed = inew < 0 ? -1 : medium(inew);
336  }
337  if (normal) {
338  *normal = Ax.normal()*norm_x*xflip + Ay.normal() * norm_y * yflip;
339  }
340  return inew;
341  }
342  else {
343  return ireg;
344  }
345  };
346 
347  EGS_Float hownear(int ireg, const EGS_Vector &src) {
348  EGS_Float x = fabs(Ax*(src-xo));
349  EGS_Float y = fabs(Ay*(src-xo));
350 
351  if (ireg < 0) {
352  return getRingDist(nreg - 1, x, y);
353  }
354  if (ireg == 0) {
355  return getRingDist(0, x, y);
356  }
357  EGS_Float outdist = getRingDist(ireg, x, y);
358  EGS_Float indist = getRingDist(ireg-1, x, y);
359  return fmin(outdist,indist);
360  };
361 
362  const string &getType() const {
363  return mytype;
364  };
365 
366  void printInfo() const {
368  egsInformation("Type = %s\n",mytype.c_str());
369  egsInformation(" midpoint of cylinders = (%g,%g,%g)\n",xo.x,xo.y,xo.z);
370  int j;
371  egsInformation(" radii along 'x' =");
372  for (j=0; j<nreg; j++) {
373  egsInformation(" %g",ax[j]);
374  }
375  egsInformation("\n");
376  egsInformation(" radii along 'y' =");
377  for (j=0; j<nreg; j++) {
378  egsInformation(" %g",ay[j]);
379  }
380  egsInformation("\n");
381  egsInformation("===================================================\n");
382  };
383 
384 private:
385 
386  bool inRing(int ireg, const EGS_Vector &src) {
387  EGS_Vector rc(src-xo);
388  EGS_Float x = fabs(Ax*rc);
389  EGS_Float y = fabs(Ay*rc);
390  if (x >= ax[ireg] || y >= ay[ireg]) {
391  return false;
392  }
393  EGS_Float dx = (x - (ax[ireg]-ar[ireg]));
394  EGS_Float dy = (y - (ay[ireg]-ar[ireg]));
395  if (dx < 0 || dy < 0) {
396  return true;
397  }
398  bool incirc = (dx*dx + dy*dy) <= ar[ireg]*ar[ireg];
399  return incirc;
400  }
401 
402  EGS_Float getRingDist(int ireg, EGS_Float x, EGS_Float y) {
403  // Either outside or inside of ring
404  EGS_Float dx = (x - (ax[ireg] - ar[ireg]));
405  EGS_Float dy = (y - (ay[ireg] - ar[ireg]));
406  if (dx > 0 && dy > 0) {
407  // Affected by curved section
408  return fabs(sqrt(dx*dx+dy*dy)-ar[ireg]);
409  }
410  else if (dy >= 0) {
411  // Y coordinate near edge
412  return fabs(y - ay[ireg]);
413  }
414  else if (dx >= 0) {
415  return fabs(x - ax[ireg]);
416  }
417  else {
418  // Inside of the corner centers
419  return fmin(fabs(x - ax[ireg]), fabs(y - ay[ireg]));
420  }
421  }
422 
423  bool findLineIntersection(int ring,
424  EGS_Float px, EGS_Float py, EGS_Float qx, EGS_Float qy,
425  EGS_Float &norm_x, EGS_Float &norm_y, EGS_Float &ux, EGS_Float &uy) {
426  if (qx == 0.0) {
427  // Straight horiz.
428  if (px <= ax[ring] - ar[ring]) {
429  norm_x = 0;
430  norm_y = 1;
431  ux = px;
432  uy = ay[ring];
433  return true;
434  }
435  else if (px <= ax[ring]) {
436  EGS_Float dx = px - (ax[ring] - ar[ring]);
437  EGS_Float dy = sqrt(ar[ring]*ar[ring]-dx*dx);
438  EGS_Float md = 1/sqrt(dx*dx+dy*dy);
439  norm_x = dx * md;
440  norm_y = dy * md;
441  ux = px;
442  uy = dy + ay[ring]- ar[ring];
443  return true;
444  }
445  else {
446  // out of bounds
447  return false;
448  }
449  }
450  else if (qy == 0.0) {
451  // Straight vert
452  if (py <= ay[ring] - ar[ring]) {
453  norm_x = 1;
454  norm_y = 0;
455  ux = ax[ring];
456  uy = py;
457  return true;
458  }
459  else if (py <= ay[ring]) {
460  EGS_Float dy = py - (ay[ring] - ar[ring]);
461  EGS_Float dx = sqrt(ar[ring]*ar[ring]-dy*dy);
462  EGS_Float md = 1/sqrt(dx*dx+dy*dy);
463  norm_x = dx * md;
464  norm_y = dy * md;
465  ux = dx + ax[ring]- ar[ring];
466  uy = py;
467  return true;
468  }
469  else {
470  // out of bounds
471  return false;
472  }
473  }
474  else {
475  // Flip again, so that both ix, iy are in the positive region
476  EGS_Float ix = px + (qx / qy) * (ay[ring] - py);
477  EGS_Float iy = py + (qy / qx) * (ax[ring] - px);
478 
479  // Now, casework:
480  bool topface = fabs(ix) <= ax[ring] - ar[ring];
481  bool sideface = fabs(iy) <= ay[ring] - ar[ring];
482 
483  if (fabs(ix) > ax[ring] + boundaryTolerance && fabs(iy) > ay[ring] + boundaryTolerance) {
484  // fast case: definite miss
485  return false;
486  }
487 
488  bool sidehit = false;
489  bool tophit = false;
490  if (topface && sideface) {
491  EGS_Float d2side = (px-ax[ring])*(px-ax[ring])+(py-iy)*(py-iy);
492  EGS_Float d2top = (py-ay[ring])*(py-ay[ring])+(px-ix)*(px-ix);
493  if (d2side < d2top) {
494  sidehit = true;
495  }
496  else {
497  tophit = true;
498  }
499  }
500  else if (topface && py > ay[ring]) {
501  tophit = true;
502  }
503  else if (sideface && px > ax[ring]) {
504  sidehit = true;
505  }
506 
507  // top/side hit not simultaneously true
508  if (tophit) {
509  ux = ix;
510  uy = ay[ring];
511  norm_x = 0;
512  norm_y = 1;
513  return true;
514  }
515  else if (sidehit) {
516  ux = ax[ring];
517  uy = iy;
518  norm_x = 1;
519  norm_y = 0;
520  return true;
521  }
522  else {
523  // Use normalized vectors for simplicity
524  EGS_Float f = 1 / sqrt(qx*qx+qy*qy);
525  EGS_Float nqx = qx * f;
526  EGS_Float nqy = qy * f;
527 
528  // Construct circle axis nearest to intersections
529  EGS_Float rx = (ax[ring] - ar[ring]);
530  EGS_Float ry = (ay[ring] - ar[ring]);
531  // Reinsert once warpage understood
532 
533  if (px <= ax[ring] && py <= ay[ring]) {
534  // Looking from inside corner
535  }
536  else if (px <= ax[ring]) {
537  // Looking from top
538  if (ix < 0) {
539  rx *= -1;
540  }
541  }
542  else if (py <= ay[ring]) {
543  // Looking from side
544  if (iy < 0) {
545  ry *= -1;
546  }
547  }
548  else {
549  // Looking from corner
550  if (ix < 0 && iy < 0) {
551  // Should never happen
552  }
553  else if (ix < 0) {
554  rx *= -1;
555  }
556  else if (iy < 0) {
557  ry *= -1;
558  }
559  }
560 
561  // S1: intersect perp. radial ray with line -- note that it's just (-y,x)
562  // S1: Find minimum distance between ray and line
563  EGS_Float s = (nqy * (px - rx) - nqx * (py - ry));
564 
565  // S2: check if int too close/far
566  if (fabs(s) <= ar[ring]) {
567  // S3: move pyth in appropriate direction along line
568  EGS_Float v = sqrt(fmax(ar[ring]*ar[ring] - s*s,0));
569  // Pick closer point... (x-product with norm is < 0)
570  norm_x = -nqy * (-s);
571  norm_y = nqx * (-s);
572 
573  if (nqx * (norm_x + nqx*v) + nqy * (norm_y + nqy*v) < 0) {
574  norm_x += nqx*v;
575  norm_y += nqy*v;
576  }
577  else {
578  norm_x -= nqx*v;
579  norm_y -= nqy*v;
580  }
581  ux = rx + norm_x;
582  uy = ry + norm_y;
583  // |norm| is expected to equal ar[ring]
584  norm_x *= 1/ar[ring];
585  norm_y *= 1/ar[ring];
586  return true;
587  }
588  else {
589  return false;
590  }
591  }
592  }
593  }
594 };
595 
596 #endif
Base geometry class. Every geometry class must be derived from EGS_BaseGeometry.
virtual void printInfo() const
Print information about this geometry.
A set of concentric rounded rectangles.
Tx Ax
The projection operator for the first ('x') axis.
EGS_Float * ar
Radii of roundings for each rect.
Ty Ay
The projection operator for the second ('y') axis.
EGS_Float * ay
Widths along the axis defined by Ay,.
EGS_Float * ax
Widths along the axis defined by Ax,.
string mytype
The cylinder type.
EGS_RoundRectCylindersT(const vector< EGS_Float > &x_wid, const vector< EGS_Float > &y_wid, const vector< EGS_Float > &rad, const EGS_Vector &position, const string &Name, const Tx &A_x, const Ty &A_y)
Construct a set of concentric rounded rectangles.
EGS_Vector xo
A point on the cylinder axis.
A class representing 3D vectors.
Definition: egs_vector.h:57
EGS_Float y
y-component
Definition: egs_vector.h:62
EGS_Float z
z-component
Definition: egs_vector.h:63
EGS_Float x
x-component
Definition: egs_vector.h:61
EGS_BaseGeometry class header file.
Global egspp functions header file.
Attempts to fix broken math header files.
EGS_Projector and EGS_2DVector class header file.
EGS_InfoFunction EGS_EXPORT egsInformation
Always use this function for reporting the progress of a simulation and any other type of information...