r117: *** empty log message ***
[ctsim.git] / libctsim / phantom.cpp
1 /*****************************************************************************
2 ** FILE IDENTIFICATION
3 ** 
4 **     Name:                   phm.cpp
5 **     Purpose:                Routines for phantom objects
6 **     Progammer:              Kevin Rosenberg
7 **     Date Started:           Aug 1984
8 **
9 **  This is part of the CTSim program
10 **  Copyright (C) 1983-2000 Kevin Rosenberg
11 **
12 **  $Id: phantom.cpp,v 1.4 2000/06/22 10:17:28 kevin Exp $
13 **
14 **  This program is free software; you can redistribute it and/or modify
15 **  it under the terms of the GNU General Public License (version 2) as
16 **  published by the Free Software Foundation.
17 **
18 **  This program is distributed in the hope that it will be useful,
19 **  but WITHOUT ANY WARRANTY; without even the implied warranty of
20 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 **  GNU General Public License for more details.
22 **
23 **  You should have received a copy of the GNU General Public License
24 **  along with this program; if not, write to the Free Software
25 **  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26 ******************************************************************************/
27
28 #include "ct.h"
29
30
31 // CLASS IDENTIFICATION
32 //   Phanton
33 //
34
35 Phantom::Phantom (void)
36 {
37   init ();
38 }
39
40
41 Phantom::Phantom (const char* const phmName)
42 {
43   init ();
44   createFromPhantom (phmName);
45 }
46
47 void 
48 Phantom::init (void)
49 {
50   m_nPElem = 0;
51   m_xmin = 1E30;
52   m_xmax = -1E30;
53   m_ymin = 1E30;
54   m_ymax = -1E30;
55   m_diameter = 0;
56   m_composition = P_PELEMS;
57   m_fail = false;
58   m_id = PHM_INVALID;
59 }
60
61 Phantom::~Phantom (void)
62 {
63   for (PElemIterator i = m_listPElem.begin(); i != m_listPElem.end(); i++) {
64     delete *i;
65   }
66 }
67
68
69 const char*
70 Phantom::convertPhantomIDToName (const PhantomID phmID)
71 {
72   const char *name = "";
73
74   if (phmID == PHM_HERMAN)
75     name = PHM_HERMAN_STR;
76   else if (phmID == PHM_BHERMAN)
77     name = PHM_BHERMAN_STR;
78   else if (phmID == PHM_ROWLAND)
79     name = PHM_ROWLAND_STR;
80   else if (phmID == PHM_BROWLAND)
81     name = PHM_BROWLAND_STR;
82   else if (phmID == PHM_UNITPULSE)
83     name = PHM_UNITPULSE_STR;
84
85   return (name);
86 }
87       
88 Phantom::PhantomID
89 Phantom::convertNameToPhantomID (const char* const phmName) 
90 {
91   PhantomID id;
92
93   if (strcasecmp (phmName, PHM_HERMAN_STR) == 0)
94     id = PHM_HERMAN;
95   else if (strcasecmp (phmName, PHM_BHERMAN_STR) == 0)
96     id = PHM_BHERMAN;
97   else if (strcasecmp (phmName, PHM_ROWLAND_STR) == 0)
98     id = PHM_ROWLAND;
99   else if (strcasecmp (phmName, PHM_BROWLAND_STR) == 0)
100     id = PHM_BROWLAND;
101   else if (strcasecmp (phmName, PHM_UNITPULSE_STR) == 0)
102     id = PHM_UNITPULSE;
103   else
104     id = PHM_INVALID;
105
106   return (id);
107 }
108   
109
110 bool
111 Phantom::createFromPhantom (const char* const phmName)
112 {
113   PhantomID phmid = convertNameToPhantomID (phmName);
114   m_name = phmName;
115
116   createFromPhantom (phmid);
117 }
118
119 bool
120 Phantom::createFromPhantom (const PhantomID phmid)
121 {
122   switch (phmid) 
123     {
124     case PHM_HERMAN:
125       addStdHerman();
126       break;
127     case PHM_BHERMAN:
128       addStdHermanBordered();
129       break;
130     case PHM_ROWLAND:
131       addStdRowland();
132       break;
133     case PHM_BROWLAND:
134       addStdRowlandBordered ();
135       break;
136     case PHM_UNITPULSE:
137       m_composition = P_UNIT_PULSE;
138       addPElem ("rectangle", 0., 0., 100., 100., 0., 0.);     // outline 
139       addPElem ("ellipse", 0., 0., 1., 1., 0., 1.);           // pulse 
140       break;
141     default:
142       sys_error (ERR_WARNING, "Illegal phantom id %d\n", phmid);
143       m_name += " -- INVALID";
144       m_fail = true;
145       return false;
146       break;
147     }
148
149   m_id = phmid;
150
151   return true;
152 }
153
154
155 /* METHOD IDENTIFICATION
156  *   createFromFile          Add PhantomElements from file
157  *
158  * SYNOPSIS
159  *   createFromFile (filename)
160  *
161  * RETURNS
162  *   true if pelem were added
163  *   false if an pelem not added
164  */
165
166 bool
167 Phantom::createFromFile (const char* const fname)
168 {
169   bool stoploop = false;
170   bool retval = false;
171   FILE *fp;
172
173   if ((fp = fopen (fname, "r")) == NULL)
174     return (false);
175
176   do {
177     double cx, cy, u, v, rot, dens;
178     char pelemtype[80];
179     int n = fscanf (fp, "%79s %lf %lf %lf %lf %lf %lf",
180                 pelemtype, &cx, &cy, &u, &v, &rot, &dens);
181     
182     if (n == EOF || n == 0) {   /* end of file */
183       stoploop = true;
184       retval = false;
185     } else if (n != 7) {
186       stoploop = true;
187       retval = false;
188     } else {
189       addPElem (pelemtype, cx, cy, u, v, rot, dens);
190       retval = true;
191     }
192   } while (stoploop == false);
193   
194   fclose (fp);
195
196   return (retval);
197 }
198
199
200 /* NAME
201  *   addPElem           Add pelem
202  *
203  * SYNOPSIS
204  *   addPElem (type, cx, cy, u, v, rot, atten)
205  *   char *type         type of pelem (box, ellipse, etc)
206  *   double cx, cy      pelem center
207  *   double u,v         pelem size
208  *   double rot         rotation angle of pelem (in degrees)
209  *   double atten       x-ray attenuation cooefficient
210  */
211
212 void 
213 Phantom::addPElem (const char *type, const double cx, const double cy, const double u, const double v, const double rot, const double atten)
214 {
215   PhantomElement *pelem = new PhantomElement (type, cx, cy, u, v, rot, atten);
216
217   m_listPElem.push_front (pelem);
218
219   // update phantom limits
220   if (m_xmin > pelem->xmin())    m_xmin = pelem->xmin();
221   if (m_xmax < pelem->xmax())    m_xmax = pelem->xmax();
222   if (m_ymin > pelem->ymin())    m_ymin = pelem->ymin();
223   if (m_ymax < pelem->ymax())    m_ymax = pelem->ymax();
224
225   if (m_diameter < pelem->diameter())
226     m_diameter = pelem->diameter();
227
228   //  m_diameter = lineLength(m_xmin, m_ymin, m_xmax, m_ymax);
229
230   m_nPElem++;
231 }
232
233
234 /*----------------------------------------------------------------------*/
235 /*                      Input-Output Routines                           */
236 /*----------------------------------------------------------------------*/
237
238
239 /* NAME
240  *   print                              Print vertices of Phantom pelems
241  *
242  * SYNOPSIS
243  *   print (phm)
244  */
245
246 void 
247 Phantom::print (void) const
248 {
249   printf("PRINTING Phantom\n\n");
250   printf("number of pelems in Phantom = %d\n", m_nPElem);
251   printf("limits: xmin=%8.2g  ymin=%8.2g  xmax=%8.2g  ymax=%8.2g\n",
252          m_xmin, m_ymin, m_xmax, m_ymax);
253   
254   for (PElemIterator i = m_listPElem.begin(); i != m_listPElem.end(); i++) {
255       printf("PELEM:\n");
256       printf("# pts=%3d atten = %7.4f   rot = %7.2f (deg)\n",
257              (*i)->nOutlinePoints(), (*i)->atten(), convertRadiansToDegrees ((*i)->rot()));
258     
259     printf("xmin=%7.3g  ymin=%7.3g  xmax=%7.3g  ymax=%7.3g\n",
260            (*i)->xmin(), (*i)->ymin(), (*i)->xmax(), (*i)->ymax());
261     
262     //    for (int i = 0; i < m_nPoints; i++)
263     //      printf("\t%8.3g    %8.3g\n", i->xOutline()[i], i->yOutline()[i]);
264   }
265 }
266
267
268 /* NAME
269  *   show               Show vector outline of Phantom to user
270  *
271  * SYNOPSIS
272  *   show (pic)
273  */
274
275 #ifdef HAVE_SGP
276 void 
277 Phantom::show (void) const
278 {
279   double wsize = m_xmax - m_xmin;
280   double xmin = m_xmin;
281   double ymin = m_ymin;
282   double xmax, ymax;
283   SGP_ID gid;
284
285   if ((m_ymax - m_ymin) > wsize)
286       wsize = m_ymax - m_ymin;
287   wsize *= 1.1;
288
289   xmax = xmin + wsize;
290   ymax = ymin + wsize; 
291   
292   printf("Drawing Phantom:\n\n");
293   printf("    data limits: %9.3g, %9.3g, %9.3g, %9.3g\n",
294          m_xmin, m_ymin, m_xmax, m_ymax);
295   printf("    window size: %9.3g, %9.3g, %9.3g, %9.3g\n",
296          xmin, ymin, xmax, ymax);
297
298   gid = sgp2_init(0, 0, "Phantom Show");
299   sgp2_window (xmin, ymin, xmax, ymax);
300
301   draw();
302
303   termgrf2();
304 }
305 #endif
306
307
308 /* NAME
309  *   draw               Draw vector outline of Phantom
310  *
311  * SYNOPSIS
312  *   draw ()
313  */
314
315 #ifdef HAVE_SGP
316 void 
317 Phantom::draw (void) const
318 {
319   for (PElemIterator i = m_listPElem.begin(); i != m_listPElem.end(); i++)
320     sgp2_polyline_abs ((*i)->xOutline(), (*i)->yOutline(), (*i)->nOutlinePoints());
321 }
322 #endif
323
324
325 /* NAME
326  *   addStdRowland              Make head phantom of S.W. Rowland
327  *
328  * REFERENCES
329  *   S. W. Rowland, "Computer Implementation of Image Reconstruction
330  *      Formulas", in "Image Reconstruction from Projections: Implementation
331  *      and Applications", edited by G. T. Herman, 1978.
332  */
333
334 void 
335 Phantom::addStdRowland (void)
336 {
337   addPElem("ellipse",  0.0000,  0.0000, 0.6900,  0.9200,   0.0,  1.00);
338   addPElem("ellipse",  0.0000, -0.0184, 0.6624,  0.8740,   0.0, -0.98);
339   addPElem("ellipse",  0.2200,  0.0000, 0.1100,  0.3100, -18.0, -0.02);
340   addPElem("ellipse", -0.2200,  0.0000, 0.1600,  0.4100,  18.0, -0.02);
341   addPElem("ellipse",  0.0000,  0.3500, 0.2100,  0.2500,   0.0,  0.01);
342   addPElem("ellipse",  0.0000,  0.1000, 0.0460,  0.0460,   0.0,  0.01);
343   addPElem("ellipse",  0.0000, -0.1000, 0.0460,  0.0460,   0.0,  0.01);
344   addPElem("ellipse", -0.0800, -0.6050, 0.0460,  0.0230,   0.0,  0.01);
345   addPElem("ellipse",  0.0000, -0.6050, 0.0230,  0.0230,   0.0,  0.01);
346   addPElem("ellipse",  0.0600, -0.6050, 0.0230,  0.0230,   0.0,  0.01);
347   addPElem("ellipse",  0.5538, -0.3858, 0.0330,  0.2060, -18.0,  0.03);
348 }
349
350 void 
351 Phantom::addStdRowlandBordered (void)
352 {
353   addStdRowland ();
354   addPElem ("ellipse", 0.000, 0.0000, 0.7500, 1.000, 0.0, 0.00);
355 }
356
357 /* NAME
358  *   addStdHerman                       Standard head phantom of G. T. Herman
359  *
360  * REFERENCES
361  *   G. T. Herman, "Image Reconstructions from Projections:  The Fundementals
362  *      of Computed Tomography", 1979.
363  */
364
365 void 
366 Phantom::addStdHerman (void)
367 {
368   addPElem("ellipse",  0.000,  1.50,  0.375, 0.3000,  90.00, -0.003);
369   addPElem("ellipse",  0.675, -0.75,  0.225, 0.1500, 140.00,  0.010);
370   addPElem("ellipse",  0.750,  1.50,  0.375, 0.2250,  50.00,  0.003);
371   addPElem("segment",  1.375, -7.50,  1.100, 0.6250,  19.20, -0.204);
372   addPElem("segment",  1.375, -7.50,  1.100, 4.3200,  19.21,  0.204);
373   addPElem("segment",  0.000, -2.25,  1.125, 0.3750,   0.00, -0.003);
374   addPElem("segment",  0.000, -2.25,  1.125, 3.0000,   0.00,  0.003);
375   addPElem("segment", -1.000,  3.75,  1.000, 0.5000, 135.00, -0.003);
376   addPElem("segment", -1.000,  3.75,  1.000, 3.0000, 135.00,  0.003);
377   addPElem("segment",  1.000,  3.75,  1.000, 0.5000, 225.00, -0.003);
378   addPElem("segment",  1.000,  3.75,  1.000, 3.0000, 225.00,  0.003);
379   addPElem("triangle", 5.025,  3.75,  1.125, 0.5000, 110.75,  0.206);
380   addPElem("triangle",-5.025,  3.75,  1.125, 0.9000,-110.75,  0.206);
381   addPElem("ellipse",  0.000,  0.00,  8.625, 6.4687,  90.00,  0.416);
382   addPElem("ellipse",  0.000,  0.00,  7.875, 5.7187,  90.00, -0.206);
383 }
384
385 void
386 Phantom::addStdHermanBordered (void)
387 {
388   addPElem("ellipse", 0., 0., 6.6, 5.9, 90., 0.);
389 }
390
391
392 /* NAME
393  *    convertToImagefile                Make image array from Phantom
394  *
395  * SYNOPSIS
396  *    pic_to_imagefile (pic, im, nsample)
397  *    Phantom& pic              Phantom definitions
398  *    ImageFile  *im            Computed pixel array
399  *    int nsample               Number of samples along each axis for each pixel
400  *                              (total samples per pixel = nsample * nsample)
401  */
402
403 void
404 Phantom::convertToImagefile (ImageFile& im, const int in_nsample, const int trace) const
405 {
406   convertToImagefile (im, in_nsample, trace, 0, im.nx());
407 }
408
409 void 
410 Phantom::convertToImagefile (ImageFile& im, const int in_nsample, const int trace, const int colStart, const int colCount) const
411 {
412   int nx = im.nx();
413   int ny = im.ny();
414   if (nx < 2 || ny < 2)
415       return;
416
417   int nsample = in_nsample;
418   if (nsample < 1)  
419     nsample = 1;
420
421   double dx = m_xmax - m_xmin;
422   double dy = m_ymax - m_ymin;
423   double xcent = m_xmin + dx / 2;
424   double ycent = m_ymin + dy / 2;
425   double phmlen = (dx > dy ? dx : dy);
426
427   double phmradius = phmlen / 2;
428
429   double xmin = xcent - phmradius;
430   double xmax = xcent + phmradius;
431   double ymin = ycent - phmradius;
432   double ymax = ycent + phmradius;
433
434   // Each pixel holds the average of the intensity of the cell with (ix,iy) at the center of the pixel
435   // Set major increments so that the last cell v[nx-1][ny-1] will start at xmax - xinc, ymax - yinc).
436   // Set minor increments so that sample points are centered in cell
437
438   double xinc = (xmax - xmin) / nx;
439   double yinc = (ymax - ymin) / ny;
440
441   double kxinc = xinc / nsample;                /* interval between samples */
442   double kyinc = yinc / nsample;
443   double kxofs = kxinc / 2;             /* offset of 1st point */
444   double kyofs = kyinc / 2;
445
446   im.setAxisExtent (xmin, xmax, ymin, ymax);
447   im.setAxisIncrement (xinc, yinc);
448
449   ImageFileArray v = im.getArray();
450
451   double x_start = xmin + (colStart * xinc);
452   for (PElemConstIterator pelem = m_listPElem.begin(); pelem != m_listPElem.end(); pelem++) {
453     double x, y, xi, yi;
454     int ix, iy, kx, ky;
455     for (ix = 0, x = x_start; ix < colCount; ix++, x += xinc) {
456       for (iy = 0, y = ymin; iy < ny; iy++, y += yinc) {
457         for (kx = 0, xi = x + kxofs; kx < nsample; kx++, xi += kxinc) {
458           for (ky = 0, yi = y + kyofs; ky < nsample; ky++, yi += kyinc)
459             if ((*pelem)->isPointInside (xi, yi, PHM_COORD) == TRUE)
460               v[ix][iy] += (*pelem)->atten();
461         } // for kx
462       } /* for iy */
463     }  /* for ix */
464   }  /* for pelem */
465   
466
467   if (nsample > 1) {
468     double factor = 1.0 / (nsample * nsample);
469
470     for (int ix = 0; ix < colCount; ix++)
471       for (int iy = 0; iy < ny; iy++)
472         v[ix][iy] *= factor;
473   }
474 }
475
476 ////////////////////////////////////////////////////////////////////////////////////////////////////////
477 // CLASS IDENTIFICATION
478 //
479 //      PhantomElement
480 //
481 // PURPOSE
482 //
483 ////////////////////////////////////////////////////////////////////////////////////////////////////////
484
485
486 PhantomElement::PhantomElement (const char *type, const double cx, const double cy, const double u, const double v, const double rot, const double atten)
487     : m_cx(cx), m_cy(cy), m_u(u), m_v(v), m_atten(atten), m_nPoints(0), m_xOutline(0), m_yOutline(0)
488 {
489   m_rot = convertDegreesToRadians (rot);   // convert angle to radians
490
491   m_type = convertNameToType (type);
492
493   makeTransformMatrices ();     // calc transform matrices between phantom and normalized phantomelement
494   makeVectorOutline ();         // calculate vector outline of pelem 
495
496   // Find maximum diameter of Object
497   double r2Max = 0;
498   for (int i = 0; i < m_nPoints; i++) {
499     double r2 = (m_xOutline[i] * m_xOutline[i]) + (m_yOutline[i] * m_yOutline[i]);
500     if (r2 > r2Max)
501       r2Max = r2;
502   }
503   m_diameter = 2 * sqrt( r2Max );
504
505   m_rectLimits[0] = m_xmin;   m_rectLimits[1] = m_ymin;
506   m_rectLimits[2] = m_xmax;   m_rectLimits[3] = m_ymax;
507 }
508
509
510 PhantomElement::~PhantomElement (void)
511 {
512     delete m_xOutline;
513     delete m_yOutline;
514 }
515
516 PhmElemType
517 PhantomElement::convertNameToType (const char* const typeName)
518 {
519     PhmElemType type = PELEM_INVALID;
520
521     if (strcasecmp (typeName, "rectangle") == 0)
522         type = PELEM_RECTANGLE;
523     else if (strcasecmp (typeName, "triangle") == 0)
524         type = PELEM_TRIANGLE;
525     else if (strcasecmp (typeName, "ellipse") == 0)
526         type = PELEM_ELLIPSE;
527     else if (strcasecmp (typeName, "sector") == 0)
528         type = PELEM_SECTOR;
529     else if (strcasecmp (typeName, "segment") == 0)
530       type = PELEM_SEGMENT;
531     else
532         sys_error (ERR_WARNING, "Unknown PhantomElement type %s [PhantomElement::PhantomElement]", type);
533
534     return (type);
535 }
536
537 void 
538 PhantomElement::makeTransformMatrices (void)
539 {
540   GRFMTX_2D temp;
541
542   // To map normalized Pelem coords to world Phantom 
543   //     scale by (u, v)                                       
544   //     rotate by rot                                  
545   //     translate by (cx, cy)                         
546
547   scale_mtx2 (m_xformObjToPhm, m_u, m_v);
548   rot_mtx2  (temp, m_rot);
549   mult_mtx2 (m_xformObjToPhm, temp, m_xformObjToPhm);
550   xlat_mtx2 (temp, m_cx, m_cy);
551   mult_mtx2 (m_xformObjToPhm, temp, m_xformObjToPhm);
552
553   // to map world Phantom coodinates to normalized PElem coords
554   //     translate by (-cx, -cy)
555   //     rotate by -rot
556   //     scale by (1/u, 1/v)
557
558   xlat_mtx2 (m_xformPhmToObj, -m_cx, -m_cy);
559   rot_mtx2  (temp, -m_rot);
560   mult_mtx2 (m_xformPhmToObj, temp, m_xformPhmToObj);
561   scale_mtx2 (temp, 1 / m_u, 1 / m_v);
562   mult_mtx2 (m_xformPhmToObj, temp, m_xformPhmToObj);
563 }
564
565
566 /* NAME
567  *   pelem_make_points          INTERNAL routine to calculate point array for an pelem
568  *
569  * SYNOPSIS
570  *   makepelempts (pelem)
571  *   PELEM *pelem       pelem whose points we are calculating
572  *
573  * NOTES
574  *   Called by phm_add_pelem()
575  */
576
577 void
578 PhantomElement::makeVectorOutline (void)
579 {
580   double radius, theta, start, stop;
581   double xfact, yfact;
582   int cpts;
583
584   m_nPoints = 0;
585   switch (m_type) {
586   case PELEM_RECTANGLE:
587     m_nPoints = 5;
588     m_xOutline = new double [m_nPoints];
589     m_yOutline = new double [m_nPoints];
590     m_xOutline[0] =-m_u;        m_yOutline[0] =-m_v;
591     m_xOutline[1] = m_u;        m_yOutline[1] =-m_v;
592     m_xOutline[2] = m_u;        m_yOutline[2] = m_v;
593     m_xOutline[3] =-m_u;        m_yOutline[3] = m_v;
594     m_xOutline[4] =-m_u;        m_yOutline[4] =-m_v;
595     break;
596   case PELEM_TRIANGLE:
597     m_nPoints = 4;
598     m_xOutline = new double [m_nPoints];
599     m_yOutline = new double [m_nPoints];
600     m_xOutline[0] =-m_u;        m_yOutline[0] = 0.0;
601     m_xOutline[1] = m_u;        m_yOutline[1] = 0.0;
602     m_xOutline[2] = 0.0;        m_yOutline[2] = m_v;
603     m_xOutline[3] =-m_u;        m_yOutline[3] = 0.0;
604     break;
605   case PELEM_ELLIPSE:
606     cpts = numCirclePoints (TWOPI);
607     m_nPoints = cpts;
608     m_xOutline = new double [m_nPoints];
609     m_yOutline = new double [m_nPoints];
610     calcEllipsePoints (m_xOutline, m_yOutline, cpts, m_u, m_v);
611     break;
612   case PELEM_SECTOR:
613     radius = sqrt(m_u * m_u + m_v * m_v);
614     theta = atan(m_u / m_v);            // angle with y-axis 
615     start = 3.0 * HALFPI - theta;
616     stop  = 3.0 * HALFPI + theta;
617     cpts = numCirclePoints (stop - start);
618     m_nPoints = 3 + cpts;
619     m_xOutline = new double [m_nPoints];
620     m_yOutline = new double [m_nPoints];
621     
622     m_xOutline[0] = 0.0;                m_yOutline[0] = m_v;
623     m_xOutline[1] =-m_u;                m_yOutline[1] = 0.0;
624     calcArcPoints (&m_xOutline[2], &m_yOutline[2], cpts, 0.0, m_v, radius, start, stop);
625     m_xOutline[cpts + 2] = 0.0;
626     m_yOutline[cpts + 2] = m_v;
627     break;
628   case PELEM_SEGMENT:
629     radius = sqrt(m_u * m_u + m_v * m_v);
630     theta = atan (m_u / m_v);           // angle with y-axis 
631     start = 3.0 * HALFPI - theta;
632     stop  = 3.0 * HALFPI + theta;
633     
634     cpts = numCirclePoints (stop - start);
635     m_nPoints = cpts + 1;
636     m_xOutline = new double [m_nPoints];
637     m_yOutline = new double [m_nPoints];
638     
639     calcArcPoints (m_xOutline, m_yOutline, cpts, 0.0, m_v, radius, start, stop);
640     m_xOutline[cpts] = -m_u;
641     m_yOutline[cpts] = 0.0;
642     break;
643   default:
644     sys_error(ERR_WARNING, "illegal pelem type %d [makeVectorOutline]", m_type);
645     return;
646   }
647   
648   rotate2d (m_xOutline, m_yOutline, m_nPoints, m_rot);
649   xlat2d (m_xOutline, m_yOutline, m_nPoints, m_cx, m_cy);
650   
651   minmax_array (m_xOutline, m_nPoints, m_xmin, m_xmax);
652   minmax_array (m_yOutline, m_nPoints, m_ymin, m_ymax);
653   
654   // increase pelem extent by SCALE_PELEM_EXTENT to eliminate chance of
655   //   missing actual pelem maximum due to polygonal sampling 
656
657   xfact = (m_xmax - m_xmin) * SCALE_PELEM_EXTENT;
658   yfact = (m_ymax - m_ymin) * SCALE_PELEM_EXTENT;
659
660   m_xmin -= xfact;
661   m_ymin -= yfact;
662   m_xmax += xfact;
663   m_ymax += yfact;
664 }
665
666
667 /* NAME
668  *   calc_arc                   Calculate outline of a arc of a circle
669  *
670  * SYNOPSIS
671  *   calc_arc (x, y, xcent, ycent, pts, r, start, stop)
672  *   double x[], y[];           Array of points
673  *   int pts                    Number of points in array
674  *   double xcent, ycent        Center of cirlce
675  *   double r                   Radius of circle
676  *   double start, stop         Beginning & ending angles
677  */
678
679 void 
680 PhantomElement::calcArcPoints (double x[], double y[], const int pts, const double xcent, const double ycent, const double r, const double start, const double stop)
681 {
682     if (r <= 0.0)
683         sys_error (ERR_WARNING, "negative or zero radius in calc_arc()");
684
685     double theta = (stop - start) / (pts - 1);  // angle incr. between points 
686     double c = cos(theta);
687     double s = sin(theta);
688   
689     x[0] = r * cos (start) + xcent;
690     y[0] = r * sin (start) + ycent;
691
692     double xp = x[0] - xcent;
693     double yp = y[0] - ycent;
694     for (int i = 1; i < pts; i++) {
695         double xc = c * xp - s * yp;
696         double yc = s * xp + c * yp;
697         x[i] = xc + xcent;
698         y[i] = yc + ycent;
699         xp = xc;  yp = yc;
700     }
701 }
702
703
704 // NAME
705 //   PhantomElement::calcEllipsePoints    Calculate outline of a ellipse
706 //
707 // SYNOPSIS
708 //   calcEllipsePoints ()
709 //
710
711
712 void 
713 PhantomElement::calcEllipsePoints (double x[], double y[], const int pts, const double u, const double v)
714 {
715     calcArcPoints (x, y, m_nPoints, 0.0, 0.0, 1.0, 0.0, TWOPI);   // make a unit circle 
716     scale2d (x, y, m_nPoints, m_u, m_v);                             // scale to ellipse 
717 }
718
719
720 /* NAME
721  *   circle_pts         Calculate number of points to use for circle segment
722  *
723  * SYNOPSIS
724  *   n = circle_pts (theta)
725  *   int n              Number of points to use for arc
726  *   double theta       Length of arc in radians
727  */
728
729 int 
730 PhantomElement::numCirclePoints (double theta)
731 {
732     theta = clamp (theta, 0., TWOPI);
733
734     return static_cast<int> (POINTS_PER_CIRCLE * theta / TWOPI + 1.5);
735 }
736
737
738 bool
739 PhantomElement::clipLineWorldCoords (double& x1, double& y1, double& x2, double &y2) const
740 {
741   /* check if ray is outside of pelem extents */
742   double cx1 = x1, cy1 = y1, cx2 = x2, cy2 = y2;
743   if (! clip_rect (cx1, cy1, cx2, cy2, m_rectLimits))
744     return false;
745         
746   // convert phantom coordinates to pelem coordinates 
747   xform_mtx2 (m_xformPhmToObj, x1, y1);
748   xform_mtx2 (m_xformPhmToObj, x2, y2);
749         
750   if (! clipLineNormalizedCoords (x1, y1, x2, y2))
751     return false;
752
753   // convert standard pelem coordinates back to phantom coordinates 
754   xform_mtx2 (m_xformObjToPhm, x1, y1);
755   xform_mtx2 (m_xformObjToPhm, x2, y2);
756
757   return true;
758 }
759
760
761 /* NAME
762  *   pelem_clip_line                    Clip pelem against an arbitrary line
763  *
764  * SYNOPSIS
765  *   pelem_clip_line (pelem, x1, y1, x2, y2)
766  *   PhantomElement& pelem;             Pelem to be clipped
767  *   double *x1, *y1, *x2, *y2  Endpoints of line to be clipped
768  *
769  * RETURNS
770  *   true   if line passes through pelem
771  *              (x1, y1, x2, y2 hold coordinates of new line)
772  *   false  if line do not pass through pelem
773  *              (x1, y1, x2, y2 are undefined)
774  */
775
776 bool
777 PhantomElement::clipLineNormalizedCoords (double& x1, double& y1, double& x2, double& y2) const
778 {
779   bool accept = false;
780
781   switch (m_type) {
782   case PELEM_RECTANGLE:
783     double rect[4];
784     rect[0] = -1.0;  rect[1] = -1.0;
785     rect[2] = 1.0;  rect[3] = 1.0;
786     accept = clip_rect (x1, y1, x2, y2, rect);
787     break;
788   case PELEM_ELLIPSE:
789     accept = clip_circle (x1, y1, x2, y2, 0.0, 0.0, 1.0, 0.0, 0.0);
790     break;
791   case PELEM_TRIANGLE:
792     accept = clip_triangle (x1, y1, x2, y2, 1.0, 1.0, true);
793     break;
794   case PELEM_SEGMENT:
795     accept = clip_segment (x1, y1, x2, y2, m_u, m_v);
796     break;
797   case PELEM_SECTOR:
798     accept = clip_sector (x1, y1, x2, y2, m_u, m_v);
799     break;
800   default:
801     sys_error (ERR_WARNING, "Illegal pelem type %d [pelem_clip_line]", m_type);
802     break;
803   }
804
805   return(accept);
806 }
807
808
809 // METHOD IDENTIFICATION 
810 //    PhantomElement::isPointInside             Check if point is inside pelem
811 //
812 // SYNOPSIS
813 //    is_point_inside (pelem, x, y, coord_type)
814 //    double x, y               Point to see if lies in pelem
815 //    int coord_type            Coordinate type (PELEM_COORD or PHM_COORD)
816 //
817 // RETURNS
818 //    true if point lies within pelem
819 //    false if point lies outside of pelem
820
821 bool
822 PhantomElement::isPointInside (double x, double y, const CoordType coord_type)
823 {
824   if (coord_type == PHM_COORD) {
825     xform_mtx2 (m_xformPhmToObj, x, y);
826   } else if (coord_type != PELEM_COORD) {
827     sys_error(ERR_WARNING, "Illegal coordinate type in pelem_is_point_inside");
828     return (false);
829   }
830
831   switch (m_type) {
832   case PELEM_RECTANGLE:
833     if (x > 1. || x < -1. || y > 1. || y < -1.)
834       return (false);
835     else
836       return (true);
837     break;
838   case PELEM_TRIANGLE:
839     if (y < 0. || y > 1. - x || y > 1. + x)
840       return (false);
841     else
842       return (true);
843     break;
844   case PELEM_ELLIPSE:
845     if (x > 1. || x < -1. || y > 1. || y < -1.)
846       return (false);
847     if (x * x + y * y > 1.)             // check if inside unit circle
848       return (false);
849     else
850       return (true);
851     break;
852
853     // for clipping segments & sectors, must NOT scale by (1/u, 1/v)
854     // because this destroys information about size of arc component 
855
856   case PELEM_SEGMENT:
857     if (x > 1. || x < -1. || y > 0.)
858         return (false);         // clip against y > 0 
859     x *= m_u;                   // put back u & v scale 
860     y *= m_v;
861     if (x * x + (y-m_v) * (y-m_v) > m_u * m_u + m_v * m_v)
862       return (false);           // clip against circle, r = sqrt(@)
863     else
864       return (true);
865     break;
866   case PELEM_SECTOR:
867       if (x > 1. || x < -1. || y > 1.)   // extent 
868       return (false);
869       if (y > 1. - x || y > 1. + x)      // triangle    
870           return (false);                      // clip against triangle 
871       x *= m_u;                // circle: put back u & v scale 
872     y *= m_v;
873     if (x * x + (y-m_v) * (y-m_v) > m_u * m_u + m_v * m_v)
874         return (false);                // clip against circle 
875     else
876       return (true);
877   break;
878   default:
879     sys_error (ERR_WARNING, "Illegal pelem type in pelem_is_point_inside()");
880     break;
881   }
882
883   return (false);
884 }
885
886