EGSnrc C++ class library  Report PIRS-898 (2021)
Iwan Kawrakow, Ernesto Mainegra-Hing, Frederic Tessier, Reid Townson and Blake Walters
egs_dynamic_shape.cpp
Go to the documentation of this file.
1 /*
2 ###############################################################################
3 #
4 # EGSnrc egs++ dynamic shape
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: Reid Townson
25 #
26 # Contributors:
27 #
28 ###############################################################################
29 */
30 
31 
37 #include "egs_dynamic_shape.h"
38 #include "egs_input.h"
39 #include "egs_functions.h"
40 #include <sstream>
41 
42 static bool EGS_DYNAMIC_SHAPE_LOCAL inputSet = false;
43 static shared_ptr<EGS_BlockInput> EGS_DYNAMIC_SHAPE_LOCAL shapeBlockInput = make_shared<EGS_BlockInput>("shape");
44 
45 extern "C" {
46 
47  static void setInputs() {
48  inputSet = true;
49 
50  setShapeInputs(shapeBlockInput);
51  shapeBlockInput->getSingleInput("library")->setValues({"egs_dynamic_shape"});
52 
53  auto shapePtr = shapeBlockInput->addBlockInput("shape");
54  setShapeInputs(shapePtr);
55 
56  shared_ptr<EGS_BlockInput> motionBlock = shapeBlockInput->addBlockInput("motion", true);
57  motionBlock->addSingleInput("control point", true, "Parameters to define motion: timeIndex xtrans ytrans ztrans xrot yrot zrot");
58  }
59 
60  EGS_DYNAMIC_SHAPE_EXPORT string getExample() {
61  string example;
62  example = {
63  R"(
64  # Example of egs_dynamic_shape
65  #:start shape:
66  library = egs_dynamic_shape
67  :start shape:
68  definition of the shape to be 'dynamic'
69  :stop shape:
70  :start motion: # units of cm and degrees
71  control point = timeIndex(1) xtrans(1) ytrans(1) ztrans(1) xrot(1) yrot(1) zrot(1)
72  control point = timeIndex(2) xtrans(2) ytrans(2) ztrans(2) xrot(2) yrot(2) zrot(2)
73  .
74  .
75  .
76  control point = timeIndex(N) xtrans(N) ytrans(N) ztrans(N) xrot(N) yrot(N) zrot(N)
77  :stop motion:
78  :stop shape:
79 )"};
80  return example;
81  }
82 
83  EGS_DYNAMIC_SHAPE_EXPORT shared_ptr<EGS_BlockInput> getInputs() {
84  if(!inputSet) {
85  setInputs();
86  }
87  return shapeBlockInput;
88  }
89 
90  EGS_DYNAMIC_SHAPE_EXPORT EGS_BaseShape *createShape(EGS_Input *input,
91  EGS_ObjectFactory *f) {
92  if (!input) {
93  egsWarning("createShape(dynamic shape): null input?\n");
94  return 0;
95  }
96  EGS_Input *ishape = input->takeInputItem("shape",false);
97  EGS_BaseShape *shape=0;;
98  if (ishape) {
99  shape = EGS_BaseShape::createShape(ishape);
100  delete ishape;
101  }
102  if (!shape) {
103  string shape_name;
104  int err = input->getInput("shape name",shape_name);
105  if (err) {
106  egsWarning("createShape(dynamic shape): no inline shape definition"
107  " and no 'shape name' keyword\n");
108  return 0;
109  }
110  shape = EGS_BaseShape::getShape(shape_name);
111  if (!shape) {
112  egsWarning("createShape(dynamic shape): no shape named %s "
113  "exists\n",shape_name.c_str());
114  return 0;
115  }
116  }
117 
118  // Now getting motion information for dynamic component
119  EGS_Input *dyninp = input->takeInputItem("motion");
120 
121  if (dyninp) {
122  EGS_DynamicShape *s = new EGS_DynamicShape(shape, dyninp, "", f);
123  s->setName(input);
124  return s;
125  }
126  else {
127  egsWarning("EGS_DynamicShape: no control points input.\n");
128  return 0;
129  }
130  }
131 
133  stringstream itos;
134  ncpts=0;
135  vector<EGS_Float> point;
136  EGS_ControlPoint cpt;
137  int err;
138  int icpts=1;
139  itos << icpts;
140  string inputTag = "control point";
141  string inputTag_backCompat = "control point " + itos.str();
142  EGS_Input *currentInput;
143  int rotsize=0;
144 
145  // Control points read one by one from motion block in dynamic geometry definition, and saved as a vector to points
146  while (true) {
147  currentInput = dyninp->takeInputItem(inputTag);
148  if (!currentInput || currentInput->getInput(inputTag, point)) {
149  currentInput = dyninp->takeInputItem(inputTag_backCompat);
150  if (!currentInput || currentInput->getInput(inputTag_backCompat, point)) {
151  delete currentInput;
152  break;
153  }
154  }
155  delete currentInput;
156 
157  // Checking the size to make sure it is a valid control point input
158  if (point.size()!=6 && point.size()!=7) {
159  egsFatal("EGS_DynamicShape: Control point %i must specify either 6 or 7 values.\n",icpts);
160  }
161  else {
162  if (ncpts == 0) {
163  rotsize=point.size();// Variable to make sure all control point definitions have consistent formats
164  }
165  if (ncpts>0 && point[0] < cpts[ncpts-1].time) {// Make sure each time index is larger than the last
166  egsFatal("EGS_DynamicShape: Time index of control point %i < time index of control point %i\n",icpts,ncpts);
167  }
168  else if (point[0] < 0.) {// Checks that time index is valid (larger than zero)
169  egsFatal("EGS_DynamicShape: Time index of control point %i < 0.0\n",icpts);
170  }
171  else if (ncpts> 0 && point.size() != rotsize) { // Checks that control point formats follow the first
172  egsFatal("EGS_DynamicShape: Rotation definition inconsistent \n");
173  }
174  else {
175  ncpts++;
176 
177  if (ncpts ==1 && point[0] > 0.0) {
178  egsWarning("EGS_DynamicShape: Time index of control point 1 > 0.0. This will generate many warning messages.\n");
179  }
180 
181  vector<EGS_Float> T_vect;// Vector storing translation information
182  vector<EGS_Float> R_vect;// Vector storing rotation information
183  // Add translation coordinates to the translation vector
184  T_vect.push_back(point[1]);
185  T_vect.push_back(point[2]);
186  T_vect.push_back(point[3]);
187  // Add rotation coordinates to rotation vector (cases differentiate cpt formats, 6 is 2 rotation parameters, 7 is 3)
188  // In each case vector order is determined by format of EGS_rotationMatrix constructor (EGS_Float arguments)
189  if (point.size()==6) {
190  R_vect.push_back(point[6]);//rotation about z
191  R_vect.push_back(point[4]);//rotation about x
192  }//2 number case
193  if (point.size()==7) {
194  R_vect.push_back(point[4]);//rotation about x
195  R_vect.push_back(point[5]);//rotation about y
196  R_vect.push_back(point[6]);//rotation about z
197  }//3 number case
198 
199  // Add it to the vector of control points
200  cpt.time = point[0];
201  cpt.trnsl = T_vect;
202  cpt.rot = R_vect;
203  cpts.push_back(cpt);// Add created control point to list of control points
204  icpts++;
205  itos.str("");
206  itos << icpts;
207  inputTag_backCompat = "control point " + itos.str();// Define next control point i string for getInput in while condition
208  }
209  }
210  }
211  if (ncpts<=1) {
212  egsFatal("EGS_DynamicShape: not enough or missing control points.\n");
213  }
214  if (cpts[ncpts-1].time == 0.0) {
215  egsFatal("EGS_DynamicShape: time index of last control point = 0. Something's wrong.\n");
216  }
217  else {
218  // Normalize time index to max. value
219  for (int i=0; i<=ncpts-1; i++) {// I changed the normalization here (and in dynamic source) to have <= instead of <. Otherwise last cpt never gets normalized, which is not an issue for final cpt time>1 but is an issue if final cpt time<1.
220  cpts[i].time /= cpts[ncpts-1].time;
221  }
222  }
223  updatePosition(0); // Sets position to initial time in egs_view upon opening
224  };
225 
227  int errg = 1;
228  EGS_ControlPoint gipt;
229 
230  // Here get source from activeapplication in order to extract time
232  while (errg) {
233  // Gets time if it's already set (otherwise gives -1).
234  ptime = app->getTimeIndex();
235 
236  if (ptime<0) {
237  // If no time is given by the source the shape will randomly sample from 0 to 1.
238  ptime = rndm->getUniform();
239 
240  // Set randomly sampled time index for all objects in the simulation
242  }
243 
244  // Now run the get coord method that will sample the cpt given to find the transformation that will be applied for the current history
245  errg = getCoord(ptime,gipt);
246  }
247 
248  // Create and set the current shape transformation using the sampled coordinates from getCoord. This is where overloaded EGS_AffineTransform is used
249  EGS_AffineTransform *tDG = EGS_AffineTransform::getTransformation(gipt.trnsl, gipt.rot);
250  shape->setTransformation(tDG);
251  delete tDG;
252 
253  // Call getNextShapePosition on base shape in case there are lower level dynamic shapes
254  shape->getNextShapePosition(rndm);
255  };
256 
257  int EGS_DynamicShape::getCoord(EGS_Float rand, EGS_ControlPoint &gipt) {
258  int iindex=0;
259  int i;
260 
261  // The following loop determines which 2 control points the current time index falls between
262  for (i=0; i<ncpts; i++) {
263  if (rand < cpts[i].time-epsilon) { // If the control point i time index is larger than the current time index, we know this is the upper bound control point (represented by iindex)
264  iindex = i;
265  break;
266  }
267  }
268 
269  if (i==ncpts) {
270  egsWarning("EGS_DynamicShape: could not locate control point.\n");
271  return 1;
272  }
273 
274  // Below 3 vectors are defined, a vector containing the lower bound translation coordinates, a vector containing the upper bound translation coordinates, and a vector for the sampled translation coordinates
275  vector<EGS_Float> translation_LB=cpts[iindex-1].trnsl;
276  vector<EGS_Float> translation_UB=cpts[iindex].trnsl;
277  vector<EGS_Float> translation_samp;
278 
279  // The following is a factor (between 0 and 1) used for sampling. Essentially, it represents the fractional position within the interval
280  EGS_Float factor = (rand-cpts[iindex-1].time)/(cpts[iindex].time-cpts[iindex-1].time);
281 
282  // Translations are given as the lower bound plus the length of the interval multiplied by the fractional position factor. So its essentially lower bound + n% of the interval length
283  translation_samp.push_back(translation_LB[0]+(translation_UB[0]-translation_LB[0])*factor);
284  translation_samp.push_back(translation_LB[1]+(translation_UB[1]-translation_LB[1])*factor);
285  translation_samp.push_back(translation_LB[2]+(translation_UB[2]-translation_LB[2])*factor);
286  // Update the translation coordinates in the current state control point object
287  gipt.trnsl=translation_samp;
288 
289  // Again 3 vectors defined, lower bound, upper bound, and sampled, this time for rotation coordinates
290  vector<EGS_Float> rotation_LB=cpts[iindex-1].rot;
291  vector<EGS_Float> rotation_UB=cpts[iindex].rot;
292  vector<EGS_Float> rotation_samp;
293 
294  // Now set rotations. Current coordinates computed as before (lowerbound+n% of interval length), but now we also convert degrees to radians
295  // These must be done case by case (since order of arguments matters)
296  if (cpts[iindex].rot.size()==2) {
297  rotation_samp.push_back((rotation_LB[0]+(rotation_UB[0]-rotation_LB[0])*factor)*(M_PI/180));
298  rotation_samp.push_back((rotation_LB[1]+(rotation_UB[1]-rotation_LB[1])*factor)*(M_PI/180));
299  }
300  else if (cpts[iindex].rot.size()==3) {
301  rotation_samp.push_back((rotation_LB[0]+(rotation_UB[0]-rotation_LB[0])*factor)*(M_PI/180));
302  rotation_samp.push_back((rotation_LB[1]+(rotation_UB[1]-rotation_LB[1])*factor)*(M_PI/180));
303  rotation_samp.push_back((rotation_LB[2]+(rotation_UB[2]-rotation_LB[2])*factor)*(M_PI/180));
304  }
305  else {
306  egsWarning("EGS_DynamicShape: Invalid number of rotation parameters\n");
307  }
308  gipt.rot=rotation_samp;
309 
310  // Update the rotation coordinates in the current state control point object
311  return 0;
312  };
313 
314 } // extern "C"
315 
A class providing affine transformations.
static EGS_AffineTransform * getTransformation(EGS_Input *inp)
Constructs an affine transformation object from the input pointed to by inp and returns a pointer to ...
Base class for advanced EGSnrc C++ applications.
static EGS_Application * activeApplication()
Get the active application.
EGS_Float getTimeIndex()
Returns the value of the time synchronization parameter.
void setTimeIndex(EGS_Float temp_time)
Base shape class. All shapes in the EGSnrc C++ class library are derived from EGS_BaseShape.
Definition: egs_shapes.h:145
static EGS_BaseShape * getShape(const string &Name)
Get a pointer to the shape named Name.
Definition: egs_shapes.cpp:64
static EGS_BaseShape * createShape(EGS_Input *inp)
Create a shape from the information pointed to by inp.
Definition: egs_shapes.cpp:51
void setTransformation(EGS_Input *inp)
Set the transformation attached to this shape.
Definition: egs_shapes.cpp:69
virtual void updatePosition(EGS_Float time)
Update the position of the shape if it is in motion.
Definition: egs_shapes.h:286
An dynamic shape.
EGS_Float ptime
Time index corresponding to particle.
int getCoord(EGS_Float rand, EGS_ControlPoint &gipt)
Extract coordinates for the next dynamic shape position.
void getNextShapePosition(EGS_RandomGenerator *rndm)
Get the next state of the dynamic shape.
void buildDynamicShape(EGS_Input *dyninp)
Build the dynamic shape using input specifications.
vector< EGS_ControlPoint > cpts
Control points.
int ncpts
Number of control points.
EGS_BaseShape * shape
Base shape made dynamic.
A class for storing information in a tree-like structure of key-value pairs. This class is used throu...
Definition: egs_input.h:182
EGS_Input * takeInputItem(const string &key, bool self=true)
Get the property named key.
Definition: egs_input.cpp:229
int getInput(const string &key, vector< string > &values) const
Assign values to an array of strings from an input identified by key.
Definition: egs_input.cpp:341
An object factory.
void setName(EGS_Input *inp)
Set the name of the object from the information provided by inp.
Base random number generator class. All random number generators should be derived from this class.
Definition: egs_rndm.h:90
EGS_Float getUniform()
Returns a random number uniformly distributed between zero (inclusive) and 1 (exclusive).
Definition: egs_rndm.h:126
A dynamic shape.
Global egspp functions header file.
EGS_Input class header file.
EGS_InfoFunction EGS_EXPORT egsFatal
Always use this function for reporting fatal errors.
const EGS_Float epsilon
The epsilon constant for floating point comparisons.
Definition: egs_functions.h:62
EGS_InfoFunction EGS_EXPORT egsWarning
Always use this function for reporting warnings.