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