home *** CD-ROM | disk | FTP | other *** search
/ Explore the World of Soft…e: Engineering & Science / Explore_the_World_of_Software_Engineering_and_Science_HRS_Software_1998.iso / programs / civil_en / dome350.exe / DOME.CPP < prev    next >
C/C++ Source or Header  |  1995-04-08  |  47KB  |  1,466 lines

  1. //$Id: dome.cpp 3.50 1995/04/08 22:16:05 PONDERMATI Released PONDERMATI $
  2.  
  3. /*
  4. DOME.CPP - A program & C++ class for the calculation of geodesic dome
  5.             properties
  6.  
  7.      Copyright (C) 1995  Richard J. Bono
  8.  
  9.     This program is free software; you can redistribute it and/or modify
  10.     it under the terms of the GNU General Public License as published by
  11.     the Free Software Foundation; either version 2 of the License, or
  12.     any later version.
  13.  
  14.     This program is distributed in the hope that it will be useful,
  15.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17.     GNU General Public License for more details.
  18.  
  19.     You should have received a copy of the GNU General Public License
  20.     along with this program; if not, write to the Free Software
  21.     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  22.  
  23.      Please direct inquiries, comments and modifications to:
  24.      Richard J. Bono
  25.      44 Augusta Rd.
  26.      Brownsville, TX 78521
  27.  
  28.      email: 70324.1712@compuserve.com
  29.                 -or-
  30.             rjbono@AOL.com
  31.  
  32. Revision history:
  33.  
  34. $Log: dome.cpp $
  35. 'Revision 3.50  1995/04/08  22:16:05  PONDERMATI
  36. 'Added Class II symmetry triangle support
  37. 'Added spherical rotations of Class I polyhdera
  38. 'Added headers & spherical coordinates to PRN output
  39. 'Added routine to test bottom face tetra rotation
  40. 'Added rounding checks to rotate functions
  41. 'Class constructor passed a input parameter structure
  42. 'Removed interactive interface
  43. 'Streamlined verbose display
  44. 'Added separate help function
  45. 'Added Class I sphere formation
  46. 'Updated exit codes to non-zero where required
  47. 'Added non-Borland C defs
  48. 'Added automatic revision tracking
  49. 'Enabled Class II symmetry triangle support
  50. '
  51. 'Revision 2.18  1995/02/12  04:54:49  PONDERMATIC
  52. 'Release Version of Dome.
  53. 'Added command line-support
  54. 'Added support for PRN & POV-Ray output files
  55. 'Rearranged many functions and parameter passing
  56. '
  57. 'Revision 2.0  1995/01/01  22:54:59  PONDERMATI
  58. 'First public release. Supports only primary symmetry triangle calculations,
  59. 'class I (Alternate) breakdowns, & DXF & ASCII file outputs.
  60. '
  61.  
  62. Acknowledgements & References:
  63. The main reference used in the creation of this code was "Geodesic Math & How
  64. to Use It" by Hugh Kenner, 1976, University of California Press.
  65. ISBN 0-520-02924-0; Library of Congress Catalog Card Number: 74-27292. Many
  66. thanks to Hugh for putting this data in an accessible format.
  67.  
  68. Also, many thanks to:
  69.     J. F. Nystrom
  70.     My wife Sonia
  71.     My daughter Kathy
  72.     Chris Fearnley
  73.     Kirby Urner
  74.         &
  75.     R. Buckminster Fuller for changing the way I view Universe.
  76.  
  77. Compliation:
  78. This program was complied and tested using Borland C++ 4.5 using the large
  79. memory model on a Gateway 2000 4DX2-50V.
  80.  
  81. The release executables were complied using Borland C++ 3.1. The program will
  82. now run on any IBM PC class machine with or withou a math coprocessor.
  83.  
  84. Support is included for non-Borland compilation. Unix patches provided thanks
  85. to Chris Fearnley & John Kirk.
  86. */
  87.  
  88. #include<iostream.h>
  89. #include<iomanip.h>
  90. #include<fstream.h>
  91. #include<stdlib.h>
  92. #include<math.h>
  93. #include<ctype.h>
  94. #include<string.h>
  95.  
  96. #ifdef __BORLANDC__
  97. #include<conio.h>
  98. #endif
  99.  
  100. #ifdef __MSDOS__
  101. #define HUGEPREFIX huge
  102. #else
  103. #define HUGEPREFIX
  104. #endif
  105.  
  106. static char rcsid[]="$Id: dome.cpp 3.50 1995/04/08 22:16:05 PONDERMATI Released PONDERMATI $";
  107.  
  108. const double RAD_TO_DEG = 57.2957795130824;
  109. const double DEG_TO_RAD = .0174532925199433;
  110.  
  111. #define BYTE unsigned char
  112. #define WORD unsigned int
  113.  
  114. //Structure containing input parameters
  115. struct parameters{
  116.     long freq;
  117.     long classt;
  118.     long polyt;
  119.     long filet;
  120.     char filename[80];
  121.     int verbose_flag;
  122.     int sphere_flag;
  123. } cmd_parm;
  124.  
  125. //Function prototypes
  126. void logo_display(void);
  127. void help_display(void);
  128. void get_cmd(struct parameters *command, int param_count, char *param[]);
  129. int main(int argc, char *argv[]);
  130.  
  131. //--------------geodesic points class
  132. class Geodesic    {
  133. private:
  134.     //Various indices, and temporary variables
  135.     long i,    j, k;
  136.     long index;
  137.     double sphi, stheta, ephi, etheta;
  138.     //actual values used in calculations set dependent on class type
  139.     long freq_calc, vertex_calc, face_calc, edges_calc;
  140.  
  141. public:
  142.     long frequency, vertex, faces, edges;
  143.     long sphere_flg, poly_face;
  144.     long vertexII, facesII, edgesII;        //class II topology
  145.     long classtype, polytype;
  146.  
  147.     //structure defines geodesic coordinate points
  148.     struct coordinates{
  149.         //vertex labels: 0,0 equals zenith
  150.         long A;
  151.         long B;
  152.         //vertex coordinates: 0,0,0 equals zenith
  153.         long xprime;
  154.         long yprime;
  155.         long zprime;
  156.  
  157.         //vertex spherical coordinates
  158.         double phi;
  159.         double theta;
  160.         //Vertex cartesian coordinates
  161.         double X;
  162.         double Y;
  163.         double Z;
  164.     };
  165.  
  166.     //structure defines chord data
  167.     struct chords{
  168.         //start point
  169.         long sA;
  170.         long sB;
  171.         double sphi;
  172.         double stheta;
  173.  
  174.         //end point
  175.         long eA;
  176.         long eB;
  177.         double ephi;
  178.         double etheta;
  179.     };
  180.  
  181.     //portability note: huge keyword was needed in order to allocate memory for
  182.     //arrays from the far heap in a MSDOS far memory model.
  183.     coordinates HUGEPREFIX *pntcrd;  //pointer to coordinate array
  184.     chords HUGEPREFIX *edgepts;      //pointer to chord array
  185.  
  186.     Geodesic(struct parameters *command);    //constructor
  187.     ~Geodesic();                               //destructor
  188.     void topology(void);                       //topological abundance function
  189.     void init_crd(void);                       //Set up coordinate values
  190.     void spherical(void);                       //calculate spherical coordinates
  191.     double chord_length(double, double, double, double); //chord length function
  192.     void chord_factor(void);                   //calculates chord factors
  193.     void save_dxf(char *);                     //save chord data in DXF format
  194.     void save_ascii(char *);                   //save all coordinate data in ASCII
  195.     void save_POV(char *);                    //save data in POV format
  196.     void save_PRN(char *);                    //Save raw data in ASCII
  197.     void display_data(void);                //Display data during program execution
  198.     double clean_float(double);                //function cleans up triag zeros
  199.     double rotate_phi(double, double, double, double);    //phi rotation function
  200.     double rotate_theta(double, double, double, double); //theta rotation function
  201.     void make_sphere(void);                    //generate data for a full sphere
  202.     long tetra_angle(void);            //Get "A" coordinate to begin correction of bottom tetra face
  203. };
  204.  
  205. //--------------geodesic constructor
  206. Geodesic::Geodesic(struct parameters *command)
  207. {
  208.  
  209.     //The constructor handles all calculation when an instance is called.
  210.     //The input parameters are the frequency of subdivision, the class
  211.     //type, the polygon type and a flag to determine whether to generate
  212.     //spherical data. This is all that is needed tospecify a geodesic dome!
  213.     //(Note: I have not included radius as size is special case and scaling
  214.     //factors can be applied. The program assumes unit radius domes).
  215.  
  216.     //initialize class variables
  217.     frequency = command->freq;
  218.     classtype = command->classt;
  219.     polytype = command->polyt;
  220.     sphere_flg = command->sphere_flag;
  221.     vertex = 0;
  222.     faces = 0;
  223.     edges = 0;
  224.  
  225.     //start calculations
  226.     topology();            //calculate symmetry triangle topological abundance
  227.     init_crd();            //initialize vertex coordinates and data structures
  228.     spherical();         //calculate spherical coordinates
  229.     chord_factor();      //determine chord factors
  230.     if(sphere_flg)
  231.         make_sphere();    //Generate full sphere coordinates if required
  232.  
  233. }
  234. //--------------geodesic destructor
  235. Geodesic::~Geodesic() {}
  236. //-------------------calculate chord factors
  237. void Geodesic::chord_factor(void)
  238. {
  239.     //Function cycles through each vertex and determines the
  240.     //chord connections. This code is not very elegant but I think
  241.     //it provides more readablity. Future incarnations may modify this to
  242.     //reduce source code size.
  243.  
  244.     index = 1;
  245.     for(i=1; i<vertex_calc; i++){
  246.         if(pntcrd[i].A == 0 && pntcrd[i].B == 0){
  247.             //point is the zenith vertex; add 10 & 11
  248.             //line #1 start point definition
  249.             edgepts[index].sA = pntcrd[i].A;
  250.             edgepts[index].sB = pntcrd[i].B;
  251.             edgepts[index].sphi = pntcrd[i].phi;
  252.             edgepts[index].stheta = pntcrd[i].theta;
  253.  
  254.             for(j=i; j<=vertex_calc; j++){
  255.                 if(pntcrd[j].A == pntcrd[i].A + 1 && pntcrd[j].B == pntcrd[i].B)
  256.                     break;
  257.             }
  258.             //end point
  259.             edgepts[index].eA = pntcrd[j].A;
  260.             edgepts[index].eB = pntcrd[j].B;
  261.             edgepts[index].ephi = pntcrd[j].phi;
  262.             edgepts[index].etheta = pntcrd[j].theta;
  263.  
  264.             index++;
  265.  
  266.             //line #2 start point definition
  267.             edgepts[index].sA = pntcrd[i].A;
  268.             edgepts[index].sB = pntcrd[i].B;
  269.             edgepts[index].sphi = pntcrd[i].phi;
  270.             edgepts[index].stheta = pntcrd[i].theta;
  271.  
  272.             for(j=i; j<=vertex_calc; j++){
  273.                 if(pntcrd[j].A == pntcrd[i].A + 1 && pntcrd[j].B == pntcrd[i].B + 1)
  274.                     break;
  275.             }
  276.             //end point
  277.             edgepts[index].eA = pntcrd[j].A;
  278.             edgepts[index].eB = pntcrd[j].B;
  279.             edgepts[index].ephi = pntcrd[j].phi;
  280.             edgepts[index].etheta = pntcrd[j].theta;
  281.  
  282.             index++;
  283.  
  284.         }
  285.         else if(pntcrd[i].A == freq_calc && pntcrd[i].B == 0){
  286.             //point is a left vertex; add 01
  287.             //line #1 start point definition
  288.             edgepts[index].sA = pntcrd[i].A;
  289.             edgepts[index].sB = pntcrd[i].B;
  290.             edgepts[index].sphi = pntcrd[i].phi;
  291.             edgepts[index].stheta = pntcrd[i].theta;
  292.  
  293.             for(j=i; j<=vertex_calc; j++){
  294.                 if(pntcrd[j].A == pntcrd[i].A && pntcrd[j].B == pntcrd[i].B + 1)
  295.                     break;
  296.             }
  297.             //end point
  298.             edgepts[index].eA = pntcrd[j].A;
  299.             edgepts[index].eB = pntcrd[j].B;
  300.             edgepts[index].ephi = pntcrd[j].phi;
  301.             edgepts[index].etheta = pntcrd[j].theta;
  302.  
  303.             index++;
  304.         }
  305.         else if(pntcrd[i].A == pntcrd[i].B){
  306.             //point is a right edge; add 10 & 11
  307.             //line #1 start point definition
  308.             edgepts[index].sA = pntcrd[i].A;
  309.             edgepts[index].sB = pntcrd[i].B;
  310.             edgepts[index].sphi = pntcrd[i].phi;
  311.             edgepts[index].stheta = pntcrd[i].theta;
  312.  
  313.             for(j=i; j<=vertex_calc; j++){
  314.                 if(pntcrd[j].A == pntcrd[i].A + 1 && pntcrd[j].B == pntcrd[i].B)
  315.                     break;
  316.             }
  317.             //end point
  318.             edgepts[index].eA = pntcrd[j].A;
  319.             edgepts[index].eB = pntcrd[j].B;
  320.             edgepts[index].ephi = pntcrd[j].phi;
  321.             edgepts[index].etheta = pntcrd[j].theta;
  322.  
  323.             index++;
  324.  
  325.             //line #2 start point definition
  326.             edgepts[index].sA = pntcrd[i].A;
  327.             edgepts[index].sB = pntcrd[i].B;
  328.             edgepts[index].sphi = pntcrd[i].phi;
  329.             edgepts[index].stheta = pntcrd[i].theta;
  330.  
  331.             for(j=i; j<=vertex_calc; j++){
  332.                 if(pntcrd[j].A == pntcrd[i].A + 1 && pntcrd[j].B == pntcrd[i].B + 1)
  333.                     break;
  334.             }
  335.             //end point
  336.             edgepts[index].eA = pntcrd[j].A;
  337.             edgepts[index].eB = pntcrd[j].B;
  338.             edgepts[index].ephi = pntcrd[j].phi;
  339.             edgepts[index].etheta = pntcrd[j].theta;
  340.  
  341.             index++;
  342.         }
  343.         else if(pntcrd[i].A == freq_calc){
  344.             //point is a bottom vertex; add 01
  345.             //line #1 start point definition
  346.             edgepts[index].sA = pntcrd[i].A;
  347.             edgepts[index].sB = pntcrd[i].B;
  348.             edgepts[index].sphi = pntcrd[i].phi;
  349.             edgepts[index].stheta = pntcrd[i].theta;
  350.  
  351.             for(j=i; j<=vertex_calc; j++){
  352.                 if(pntcrd[j].A == pntcrd[i].A  && pntcrd[j].B == pntcrd[i].B + 1)
  353.                     break;
  354.             }
  355.             //end point
  356.             edgepts[index].eA = pntcrd[j].A;
  357.             edgepts[index].eB = pntcrd[j].B;
  358.             edgepts[index].ephi = pntcrd[j].phi;
  359.             edgepts[index].etheta = pntcrd[j].theta;
  360.  
  361.             index++;
  362.         }
  363.         else if(pntcrd[i].B == 0){
  364.             //point is a right edge; add 01, 10 & 11
  365.             //line #1 start point definition
  366.             edgepts[index].sA = pntcrd[i].A;
  367.             edgepts[index].sB = pntcrd[i].B;
  368.             edgepts[index].sphi = pntcrd[i].phi;
  369.             edgepts[index].stheta = pntcrd[i].theta;
  370.  
  371.             for(j=i; j<=vertex_calc; j++){
  372.                 if(pntcrd[j].A == pntcrd[i].A && pntcrd[j].B == pntcrd[i].B + 1)
  373.                     break;
  374.             }
  375.             //end point
  376.             edgepts[index].eA = pntcrd[j].A;
  377.             edgepts[index].eB = pntcrd[j].B;
  378.             edgepts[index].ephi = pntcrd[j].phi;
  379.             edgepts[index].etheta = pntcrd[j].theta;
  380.  
  381.             index++;
  382.  
  383.             //line #2 start point definition
  384.             edgepts[index].sA = pntcrd[i].A;
  385.             edgepts[index].sB = pntcrd[i].B;
  386.             edgepts[index].sphi = pntcrd[i].phi;
  387.             edgepts[index].stheta = pntcrd[i].theta;
  388.  
  389.             for(j=i; j<=vertex_calc; j++){
  390.                 if(pntcrd[j].A == pntcrd[i].A + 1 && pntcrd[j].B == pntcrd[i].B)
  391.                     break;
  392.             }
  393.             //end point
  394.             edgepts[index].eA = pntcrd[j].A;
  395.             edgepts[index].eB = pntcrd[j].B;
  396.             edgepts[index].ephi = pntcrd[j].phi;
  397.             edgepts[index].etheta = pntcrd[j].theta;
  398.  
  399.             index++;
  400.  
  401.             //line #3 start point definition
  402.             edgepts[index].sA = pntcrd[i].A;
  403.             edgepts[index].sB = pntcrd[i].B;
  404.             edgepts[index].sphi = pntcrd[i].phi;
  405.             edgepts[index].stheta = pntcrd[i].theta;
  406.  
  407.             for(j=i; j<=vertex_calc; j++){
  408.                 if(pntcrd[j].A == pntcrd[i].A + 1 && pntcrd[j].B == pntcrd[i].B + 1)
  409.                     break;
  410.             }
  411.             //end point
  412.             edgepts[index].eA = pntcrd[j].A;
  413.             edgepts[index].eB = pntcrd[j].B;
  414.             edgepts[index].ephi = pntcrd[j].phi;
  415.             edgepts[index].etheta = pntcrd[j].theta;
  416.  
  417.             index++;
  418.         }
  419.         else {
  420.             //point is an interior vertex; add 01, 10 & 11
  421.             //line #1 start point definition
  422.             edgepts[index].sA = pntcrd[i].A;
  423.             edgepts[index].sB = pntcrd[i].B;
  424.             edgepts[index].sphi = pntcrd[i].phi;
  425.             edgepts[index].stheta = pntcrd[i].theta;
  426.  
  427.             for(j=i; j<=vertex_calc; j++){
  428.                 if(pntcrd[j].A == pntcrd[i].A && pntcrd[j].B == pntcrd[i].B + 1)
  429.                     break;
  430.             }
  431.             //end point
  432.             edgepts[index].eA = pntcrd[j].A;
  433.             edgepts[index].eB = pntcrd[j].B;
  434.             edgepts[index].ephi = pntcrd[j].phi;
  435.             edgepts[index].etheta = pntcrd[j].theta;
  436.  
  437.             index++;
  438.  
  439.             //line #2 start point definition
  440.             edgepts[index].sA = pntcrd[i].A;
  441.             edgepts[index].sB = pntcrd[i].B;
  442.             edgepts[index].sphi = pntcrd[i].phi;
  443.             edgepts[index].stheta = pntcrd[i].theta;
  444.  
  445.             for(j=i; j<=vertex_calc; j++){
  446.                 if(pntcrd[j].A == pntcrd[i].A + 1 && pntcrd[j].B == pntcrd[i].B)
  447.                     break;
  448.             }
  449.             //end point
  450.             edgepts[index].eA = pntcrd[j].A;
  451.             edgepts[index].eB = pntcrd[j].B;
  452.             edgepts[index].ephi = pntcrd[j].phi;
  453.             edgepts[index].etheta = pntcrd[j].theta;
  454.  
  455.             index++;
  456.  
  457.             //line #3 start point definition
  458.             edgepts[index].sA = pntcrd[i].A;
  459.             edgepts[index].sB = pntcrd[i].B;
  460.             edgepts[index].sphi = pntcrd[i].phi;
  461.             edgepts[index].stheta = pntcrd[i].theta;
  462.  
  463.             for(j=i; j<=vertex_calc; j++){
  464.                 if(pntcrd[j].A == pntcrd[i].A + 1 && pntcrd[j].B == pntcrd[i].B + 1)
  465.                     break;
  466.             }
  467.             //end point
  468.             edgepts[index].eA = pntcrd[j].A;
  469.             edgepts[index].eB = pntcrd[j].B;
  470.             edgepts[index].ephi = pntcrd[j].phi;
  471.             edgepts[index].etheta = pntcrd[j].theta;
  472.  
  473.             index++;
  474.  
  475.         }
  476.     }
  477.  
  478. }
  479. //--------------------clean floating point numbers
  480. double Geodesic::clean_float(double fp_number)
  481. {
  482.         //function cleans floating point results close to but not quite zero
  483.         if((fp_number < 0.0) & (fp_number > -0.0000000000001))
  484.             fp_number = fabs(fp_number);
  485.  
  486.         return fp_number;
  487. }
  488. //-------------------save ASCII PRN format
  489. void Geodesic::save_PRN(char *filename)
  490. {
  491.     double sX, sY, sZ, eX, eY, eZ;
  492.  
  493.     //This function saves the spherical & cartesian data to an
  494.     //comma-delimited ASCII PRN file. This file can be imported into
  495.     //most spreadsheets an the like.
  496.     ofstream PRN(filename);
  497.  
  498.     //Set field widths
  499.     PRN << setiosflags(ios::fixed) << setw(8) << setprecision(6);
  500.  
  501.     PRN << "Spherical Vertexia Coordinates" << '\n';
  502.     //output PRN data...start with vertexia in spherical coordinates...
  503.     for(j=1; j <= poly_face; j++){
  504.         PRN << "Polyhedron Face #" << j << '\n';
  505.         for(i=(vertex_calc * (j-1)) + 1; i <= (vertex_calc * j); i++)
  506.             //Save data
  507.             PRN << pntcrd[i].phi << ", " << pntcrd[i].theta << '\n';
  508.     }
  509.     PRN << '\n';
  510.  
  511.     PRN << "Cartesian Vertexia Coordinates" << '\n';
  512.     //...then in XYZ coordinates
  513.     for(j=1; j <= poly_face; j++){
  514.         PRN << "Polyhedron Face #" << j << '\n';
  515.         for(i=(vertex_calc * (j-1)) + 1; i <= (vertex_calc * j); i++){
  516.             //convert spherical to cartesian
  517.             sX = clean_float(cos(pntcrd[i].phi * DEG_TO_RAD) * sin(pntcrd[i].theta * DEG_TO_RAD));
  518.             sY = clean_float(sin(pntcrd[i].phi * DEG_TO_RAD) * sin(pntcrd[i].theta * DEG_TO_RAD));
  519.             sZ = clean_float(cos(pntcrd[i].theta * DEG_TO_RAD));
  520.             //Save data
  521.             PRN << sX << ", " << sY << ", " << sZ << '\n';
  522.         }
  523.     }
  524.  
  525.     PRN << '\n';
  526.  
  527.     PRN << "Spherical Chord Coordinates" << '\n';
  528.     //Now do the chords...first in spherical coordinate pairs...
  529.     for(j=1; j <= poly_face; j++){
  530.         PRN << "Polyhedron Face #" << j << '\n';
  531.         for(i=(edges_calc * (j-1)) + 1; i <= (edges_calc * j); i++)
  532.             //save data
  533.             PRN << edgepts[i].sphi << ", " << edgepts[i].stheta << ", "
  534.                 << edgepts[i].ephi << ", " << edgepts[i].etheta << '\n';
  535.     }
  536.  
  537.     PRN << '\n';
  538.  
  539.     PRN << "Cartesian Chord Coordinates" << '\n';
  540.     //...then in XYZ...
  541.     for(j=1; j <= poly_face; j++){
  542.         PRN << "Polyhedron Face #" << j << '\n';
  543.         for(i=(edges_calc * (j-1)) + 1; i <= (edges_calc * j); i++){
  544.             //convert spherical to cartesian
  545.             sX = clean_float(cos(edgepts[i].sphi * DEG_TO_RAD) * sin(edgepts[i].stheta * DEG_TO_RAD));
  546.             sY = clean_float(sin(edgepts[i].sphi * DEG_TO_RAD) * sin(edgepts[i].stheta * DEG_TO_RAD));
  547.             sZ = clean_float(cos(edgepts[i].stheta * DEG_TO_RAD));
  548.             eX = clean_float(cos(edgepts[i].ephi * DEG_TO_RAD) * sin(edgepts[i].etheta * DEG_TO_RAD));
  549.             eY = clean_float(sin(edgepts[i].ephi * DEG_TO_RAD) * sin(edgepts[i].etheta * DEG_TO_RAD));
  550.             eZ = clean_float(cos(edgepts[i].etheta * DEG_TO_RAD));
  551.             //save data
  552.             PRN << sX << ", " << sY << ", " << sZ << ", "
  553.                  << eX << ", " << eY << ", " << eZ << '\n';
  554.         }
  555.     }
  556.  
  557.     PRN.close();
  558.  
  559. }
  560. //-------------------save POV-Ray file
  561. void Geodesic::save_POV(char *filename)
  562. {
  563.     double sX, sY, sZ, eX, eY, eZ;
  564.  
  565.     //save chord data for symmetry triangle to POV file
  566.     //filename should include .POV extension on MS-DOS systems
  567.  
  568.     ofstream POV(filename);
  569.     //Set field widths
  570.     POV << setiosflags(ios::fixed) << setw(8) << setprecision(6);
  571.  
  572.     //set up file header
  573.     POV << "//POV-Ray script" << '\n';
  574.     POV << "#include" << '"' << "colors.inc" << '"' << '\n' << '\n';
  575.     POV << "#declare Cam_factor = 6" << '\n';
  576.     POV << "#declare Camera_X = 1 * Cam_factor" << '\n';
  577.     POV << "#declare Camera_Y = 0.5 * Cam_factor" << '\n';
  578.     POV << "#declare Camera_Z = -0.3 * Cam_factor" << '\n';
  579.  
  580.     POV << "camera { location  <Camera_X, Camera_Y, Camera_Z>" << '\n';
  581.     POV << "        up        <0, 1.0,  0>    right     <-1.33, 0,  0>" << '\n';
  582.     POV << "        direction <0, 0,  3>      look_at   <0, 0, 0> }" << '\n' << '\n';
  583.  
  584.     POV << "light_source { <Camera_X - 2, Camera_Y + 5 , Camera_Z + 5> color White }" << '\n';
  585.     POV << "light_source { <Camera_X - 2, Camera_Y + 5 , Camera_Z - 3> color White }" << '\n';
  586.  
  587.     POV << '\n' << "// Background:" << '\n';
  588.     POV << "background {color Gray70}" << '\n' << '\n';
  589.  
  590.     POV << "// POV script for DOME: " << '\n' << '\n';
  591.  
  592.     POV << "union {" << '\n';
  593.  
  594.     //output POV data...start with spheres at vertexia points...
  595.     for(i=1; i<=vertex_calc * poly_face; i++){
  596.         //convert spherical to cartesian
  597.         sX = clean_float(cos(pntcrd[i].phi * DEG_TO_RAD) * sin(pntcrd[i].theta * DEG_TO_RAD));
  598.         sY = clean_float(sin(pntcrd[i].phi * DEG_TO_RAD) * sin(pntcrd[i].theta * DEG_TO_RAD));
  599.         sZ = clean_float(cos(pntcrd[i].theta * DEG_TO_RAD));
  600.         //Save data
  601.         POV << "sphere{<" << sX << ", " << sY << ", " << sZ;
  602.         POV << ">,0.04 pigment {Blue} no_shadow}" << '\n';
  603.     }
  604.     POV << '\n';
  605.  
  606.     //Now do the chords
  607.     for(i=1; i<=edges_calc * poly_face; i++){
  608.         //convert spherical to cartesian
  609.         sX = clean_float(cos(edgepts[i].sphi * DEG_TO_RAD) * sin(edgepts[i].stheta * DEG_TO_RAD));
  610.         sY = clean_float(sin(edgepts[i].sphi * DEG_TO_RAD) * sin(edgepts[i].stheta * DEG_TO_RAD));
  611.         sZ = clean_float(cos(edgepts[i].stheta * DEG_TO_RAD));
  612.         eX = clean_float(cos(edgepts[i].ephi * DEG_TO_RAD) * sin(edgepts[i].etheta * DEG_TO_RAD));
  613.         eY = clean_float(sin(edgepts[i].ephi * DEG_TO_RAD) * sin(edgepts[i].etheta * DEG_TO_RAD));
  614.         eZ = clean_float(cos(edgepts[i].etheta * DEG_TO_RAD));
  615.         //save data
  616.         POV << "cylinder{<" << sX << ", " << sY << ", " << sZ << ">, ";
  617.         POV << "<" << eX << ", " << eY << ", " << eZ << ">,0.03 pigment {Red} no_shadow}" << '\n';
  618.     }
  619.     POV << "rotate <0, 0, 0> }" << '\n';
  620.  
  621.     POV.close();
  622.  
  623. }
  624. //-------------------save DXF file
  625. void Geodesic::save_dxf(char *filename)
  626. {
  627.     double sX, sY, sZ, eX, eY, eZ;
  628.  
  629.     //save chord data for symmetry triangle to DXF file
  630.     //filename should include .DXF extension on MS-DOS systems
  631.     //For full spheres each face is saved on a different level...
  632.  
  633.     ofstream DXF(filename);
  634.     //output DXF data
  635.     DXF << "0" << '\n';
  636.     DXF << "SECTION" << '\n';
  637.     DXF << "2" << '\n';
  638.     DXF << "ENTITIES" << '\n';
  639.     //Set field widths
  640.     DXF << setiosflags(ios::fixed) << setw(8) << setprecision(6);
  641.  
  642.     for(j=1; j<=poly_face; j++){
  643.         for(i=(edges_calc * (j-1)) + 1; i<=(edges_calc * j); i++){
  644.             //convert spherical to cartesian
  645.             sX = clean_float(cos(edgepts[i].sphi * DEG_TO_RAD) * sin(edgepts[i].stheta * DEG_TO_RAD));
  646.             sY = clean_float(sin(edgepts[i].sphi * DEG_TO_RAD) * sin(edgepts[i].stheta * DEG_TO_RAD));
  647.             sZ = clean_float(cos(edgepts[i].stheta * DEG_TO_RAD));
  648.             eX = clean_float(cos(edgepts[i].ephi * DEG_TO_RAD) * sin(edgepts[i].etheta * DEG_TO_RAD));
  649.             eY = clean_float(sin(edgepts[i].ephi * DEG_TO_RAD) * sin(edgepts[i].etheta * DEG_TO_RAD));
  650.             eZ = clean_float(cos(edgepts[i].etheta * DEG_TO_RAD));
  651.             //save data
  652.             DXF << "0" << '\n';
  653.             DXF << "LINE" << '\n';
  654.             DXF << "8" << '\n';
  655.             DXF << j << '\n';
  656.             DXF << "10" << '\n';
  657.             DXF << sX << '\n';
  658.             DXF << "20" << '\n';
  659.             DXF << sY << '\n';
  660.             DXF << "30" << '\n';
  661.             DXF << sZ << '\n';
  662.             DXF << "11" << '\n';
  663.             DXF << eX << '\n';
  664.             DXF << "21" << '\n';
  665.             DXF << eY << '\n';
  666.             DXF << "31" << '\n';
  667.             DXF << eZ << '\n';
  668.         }
  669.     }
  670.     DXF << "0" << '\n';
  671.     DXF << "ENDSEC" << '\n';
  672.     DXF << "0" << '\n';
  673.     DXF << "EOF" << '\n';
  674.  
  675.     DXF.close();
  676.  
  677. }
  678. //-------------------save data to ASCII file
  679. void Geodesic::save_ascii(char *filename)
  680. {
  681.     //function to save data to ASCII data file
  682.     //WARNING: CAN produce very large listings!
  683.     //save chord data for symmetry triangle to DAT file
  684.     //filename should include .DAT extension on MS-DOS systems
  685.     //Only data for the symmetry triangle is saved
  686.  
  687.     double sphi, stheta, ephi, etheta;
  688.  
  689.     ofstream ASCII(filename);
  690.     //output ASCII report data
  691.     ASCII << "Geodesic Dome Data" << '\n';
  692.     ASCII << "----------------Dome Parameters-------------------------" << '\n';
  693.     ASCII << "Polyhedron Type: ";
  694.     if(polytype == 1)
  695.         ASCII << "Icosahedron" << '\n';
  696.     else if(polytype == 2)
  697.         ASCII << "Octahedron" << '\n';
  698.     else
  699.         ASCII << "Tetrahedron" << '\n';
  700.     ASCII << "Class ";
  701.     if(classtype == 1)
  702.         ASCII << "I" << '\n';
  703.     else
  704.         ASCII << "II" << '\n';
  705.     ASCII << "Frequency: " << frequency << '\n';
  706.     ASCII << "----------------Symmetry Triangle Data------------------" << '\n';
  707.     ASCII << "Vertexia: " << vertex << '\n';
  708.     ASCII << "Edges: " << edges << '\n';
  709.     ASCII << "Faces: " << faces << '\n';
  710.     ASCII << "-----------Symmetry Triangle Spherical Coordinates------" << '\n';
  711.     ASCII << "A" << '\t' << "B" << '\t' << "phi" << '\t' << "theta" << '\n';
  712.  
  713.     for(i=1; i<=vertex_calc; i++){
  714.         ASCII << setiosflags(ios::right) << setw(3);
  715.         ASCII << pntcrd[i].A << '\t';
  716.         ASCII << pntcrd[i].B << '\t';
  717.         ASCII << setw(11) << setprecision(8) << setiosflags(ios::fixed);
  718.         ASCII << pntcrd[i].phi << '\t';
  719.         ASCII << pntcrd[i].theta << '\n';
  720.     }
  721.     ASCII << '\n';
  722.     ASCII << "----------Symmetry Triangle Chord Data------------------" << '\n';
  723.     ASCII << "AB start/AB end" << '\t' << "Chord Length" << '\n';
  724.     for(i=1; i<=edges_calc; i++){
  725.         ASCII << setiosflags(ios::right) << setw(3);
  726.         ASCII << edgepts[i].sA << "," << edgepts[i].sB << "/";
  727.         ASCII << edgepts[i].eA << "," << edgepts[i].eB << '\t';
  728.         ASCII << setw(12) << setprecision(8) << setiosflags(ios::fixed);
  729.         sphi = edgepts[i].sphi;
  730.         stheta = edgepts[i].stheta;
  731.         ephi = edgepts[i].ephi;
  732.         etheta = edgepts[i].etheta;
  733.         ASCII << chord_length(sphi, stheta, ephi, etheta) << '\n';
  734.     }
  735.     ASCII << "End Dome Data" << '\n';
  736.  
  737.     ASCII.close();
  738. }
  739. //-------------------calculate chord lengths
  740. double Geodesic::chord_length(double sphi, double stheta, double ephi, double etheta)
  741. {
  742.     //Function returns distance between two points given their
  743.     //spherical coordinates. Radius is fixed at one.
  744.  
  745.     double result;
  746.  
  747.     result = pow((2-2 * (cos(stheta * DEG_TO_RAD) * cos(etheta * DEG_TO_RAD) +
  748.         cos((sphi-ephi) *DEG_TO_RAD) * sin(stheta * DEG_TO_RAD) *
  749.             sin(etheta * DEG_TO_RAD))),0.5);
  750.  
  751.     return result;
  752.  
  753. }
  754. //------------------phi rotation function
  755. double Geodesic::rotate_phi(double phi, double phi1, double theta1, double theta2)
  756. {
  757.         //phi half of the rotation formula given in Kenner's "Geodesic Math &
  758.         //How to Use it" Kenner credits this formula to Professor George Owen
  759.  
  760.         double result;
  761.  
  762.         result = (sin(theta1 * DEG_TO_RAD)* sin((phi-phi1) * DEG_TO_RAD)) /
  763.                         sin(theta2 * DEG_TO_RAD);
  764.  
  765.         //Apply correction for round-off errors
  766.         if(result > 1.0)
  767.             result = 1.0;
  768.         else if(result < -1.0)
  769.             result = -1.0;
  770.  
  771.         return asin(result) * RAD_TO_DEG;
  772.  
  773. }
  774. //------------------theta rotation function
  775. double Geodesic::rotate_theta(double phi, double phi1, double theta, double theta1)
  776. {
  777.         //theta half of the rotation formula given in Kenner's "Geodesic Math &
  778.         //How to Use it" Kenner credits this formula to Professor George Owen
  779.         double result;
  780.  
  781.         result = cos(theta1 * DEG_TO_RAD) * cos(theta * DEG_TO_RAD) +
  782.                         sin(theta1 * DEG_TO_RAD) * sin(theta * DEG_TO_RAD) *
  783.                             cos((phi-phi1) * DEG_TO_RAD);
  784.  
  785.         //Apply correction for round-off errors
  786.         if(result > 1.0)
  787.             result = 1.0;
  788.         else if(result < -1.0)
  789.             result = -1.0;
  790.  
  791.         return acos(result) * RAD_TO_DEG;
  792.  
  793. }
  794. //--------------calculate topological abundance of symmetry triangle
  795. void Geodesic::topology(void)
  796. {
  797.  
  798.     //This class function calculates the topological abundance
  799.     //of the symmetry triangle.
  800.  
  801.     //calculate the number of vertexia & faces in symmetry triangle
  802.     //This is based on the class type
  803.     //First the polyhedron face...
  804.  
  805.     for(i=1; i<=frequency; i++){
  806.         if(i == 1)
  807.             vertex = 3;
  808.         else
  809.             vertex += (i + 1);
  810.     }
  811.     faces = frequency * frequency;
  812.     edges = (vertex + faces) - 1;
  813.  
  814.     //if class II then calculate the class II symmetry triangle topology.
  815.     //The class II triangle is NOT the polyhedron face!
  816.     if(classtype == 2){
  817.         for(i=1; i<=frequency/2; i++){
  818.             if(i == 1)
  819.                 vertexII = 3;
  820.             else
  821.                 vertexII += (i + 1);
  822.         }
  823.         facesII = (frequency/2) * (frequency/2);
  824.         edgesII = (vertexII + facesII) - 1;
  825.     }
  826. }
  827. //-------------initialize coordinate array by setting vertex points
  828. void Geodesic::init_crd(void)
  829. {
  830.     //this class function first allocates memory for the coordinate
  831.     //and chord arrays. The vertex labels and vertex coordinates are then
  832.     //calculated. See chapter 12 of Kenner's "Geodesic Math & How to Use
  833.     //it" for more specifics on the labeling & coordinates
  834.  
  835.     //Set the topology dependent on class type
  836.     if(classtype == 1){
  837.         vertex_calc = vertex;
  838.         edges_calc = edges;
  839.         freq_calc = frequency;
  840.     }
  841.     else if(classtype == 2){
  842.         vertex_calc =vertexII;
  843.         edges_calc = edgesII;
  844.         freq_calc = frequency / 2;
  845.     }
  846.  
  847.  
  848.     //Determine number of faces required if a full sphere is to be generated
  849.     if(sphere_flg){
  850.         if(polytype == 1)
  851.             poly_face = 20;        //Icosa = 20 faces
  852.         else if(polytype == 2)
  853.             poly_face = 8;        //Octa = 8 faces
  854.         else
  855.             poly_face = 4;        //Tetra = 4 faces
  856.     }
  857.     else
  858.         poly_face = 1;            //Symmetry triangle only
  859.  
  860.     //Memory is allocated for the full polyhedron face. Create array object for vertexia
  861.     //In the case of a sphere, memory is allocated for everything
  862.     pntcrd = new HUGEPREFIX coordinates[(vertex + 1) * poly_face];
  863.     if (pntcrd == NULL){
  864.         logo_display();
  865.         cout << "Insufficient memory for coordinate array --- Execution Terminating" << '\n';
  866.         exit(1);
  867.     }
  868.  
  869.     //create array object for edges
  870.     edgepts = new HUGEPREFIX chords[(edges + 1) * poly_face];
  871.     if (edgepts == NULL){
  872.         logo_display();
  873.         cout << "Insufficient memory for chord array --- Execution Terminating" << '\n';
  874.         exit(1);
  875.     }
  876.  
  877.     //Initialize vertex labels and coordinates
  878.     index = 1;
  879.     for(i=0; i<=frequency; i++){
  880.         for(j=0; j<=i; j++){
  881.             pntcrd[index].A = i;
  882.             pntcrd[index].B = j;
  883.             pntcrd[index].xprime = j;
  884.             pntcrd[index].yprime = i - j;
  885.             pntcrd[index].zprime = frequency - i;
  886.             index++;
  887.         }
  888.     }
  889. }
  890. //-------------Determine "A" Coordinate of bottom tetra face-----------
  891. long Geodesic::tetra_angle(void)
  892. {
  893.         //This function returns the "A" coordinate of the bottom tetrahedron face
  894.         //where correction of the phi angle must begin. It is the result of quirks
  895.         //in professor owens rotation formulae. I don't find this code very elegant
  896.         //but it does provide the proper results.
  897.  
  898.         double sX, sphi, stheta;
  899.         double eX, ephi, etheta;
  900.  
  901.         //Save the 0,0 coordinate as the start point and convert to an X value
  902.         stheta = rotate_theta(240.0, pntcrd[1].phi, acos(-1.0/3.0) * RAD_TO_DEG,
  903.                         pntcrd[1].theta);
  904.         sphi = rotate_phi(240.0, pntcrd[1].phi, pntcrd[1].theta, stheta);
  905.         sX = clean_float(cos(sphi * DEG_TO_RAD) * sin(stheta * DEG_TO_RAD));
  906.  
  907.         //The goal of this routine is to find the "A" coordinate in which the
  908.         //difference between the current X and the last X is positive. We start
  909.         //at 0,0 then proceed down the tetra face edge until this is found
  910.         //(i.e. 0,0 to 1,0; 1,0 to 2,0; 2,0 to 3,0;...f-1,0 to f,0)
  911.  
  912.         index = 1;
  913.         for(i=2; i<=freq_calc; i++){
  914.             index += (i-1);    //index given the array location of the tetra edge point
  915.  
  916.             //Save the next coordinate as the end point and convert to an X value
  917.             etheta = rotate_theta(240.0, pntcrd[index].phi, acos(-1.0/3.0) * RAD_TO_DEG,
  918.                             pntcrd[index].theta);
  919.             ephi = rotate_phi(240.0, pntcrd[index].phi, pntcrd[index].theta, etheta);
  920.  
  921.             eX = clean_float(cos(ephi * DEG_TO_RAD) * sin(etheta * DEG_TO_RAD));
  922.  
  923.             if((eX - sX) > 0.0)
  924.                 return pntcrd[index].A;
  925.             else
  926.                 sX = eX;
  927.         }
  928.  
  929.         //Handle special cases of frequency < 5
  930.         if(freq_calc == 4)
  931.             return 3;
  932.         if(freq_calc == 1)
  933.             return 1;
  934.         else
  935.             return 2;
  936.  
  937. }
  938. //-------------rotate symmetry triangle into sphere--------------------
  939. void Geodesic::make_sphere(void)
  940. {
  941.     //Rotates chords & points to provide for full sphere
  942.     //Set the topology dependent on class type
  943.     if(classtype == 1){
  944.         vertex_calc = vertex;
  945.         edges_calc = edges;
  946.     }
  947.     else if(classtype == 2){
  948.         vertex_calc =vertexII;
  949.         edges_calc = edgesII;
  950.     }
  951.  
  952.     if(polytype == 1 && classtype == 1){
  953.         //Do downward pointing triangle
  954.         index = edges_calc + 1;
  955.         for(j=1; j<=edges_calc; j++){
  956.             edgepts[index].stheta = rotate_theta(36.0, edgepts[j].sphi, (180.0 - (atan(2) * RAD_TO_DEG)),
  957.                         edgepts[j].stheta);
  958.             edgepts[index].sphi = (rotate_phi(36.0, edgepts[j].sphi, edgepts[j].stheta,
  959.                         edgepts[index].stheta) + 36.0);
  960.             edgepts[index].etheta = rotate_theta(36.0, edgepts[j].ephi, (180.0 - (atan(2) * RAD_TO_DEG)),
  961.                         edgepts[j].etheta);
  962.             edgepts[index].ephi = (rotate_phi(36.0, edgepts[j].ephi, edgepts[j].etheta,
  963.                         edgepts[index].etheta) + 36.0);
  964.             index++;
  965.         }
  966.         //Rotate triangles #1 & #2 4 times
  967.         for(i=1; i<=4; i++){
  968.             for(j=1; j<=edges_calc * 2; j++){
  969.                   edgepts[index].sphi = edgepts[j].sphi + 72.0 * i;
  970.                   edgepts[index].stheta = edgepts[j].stheta;
  971.                   edgepts[index].ephi = edgepts[j].ephi + 72.0 * i;
  972.                   edgepts[index].etheta = edgepts[j].etheta;
  973.                   index++;
  974.             }
  975.         }
  976.         //Rotate 10 triangles by 180 degrees of theta
  977.         for(j=1; j<=edges_calc * 10; j++){
  978.               edgepts[index].sphi = edgepts[j].sphi + 36.0;
  979.               edgepts[index].stheta = 180.0 - edgepts[j].stheta;
  980.               edgepts[index].ephi = edgepts[j].ephi + 36.0;
  981.               edgepts[index].etheta = 180.0 - edgepts[j].etheta;
  982.               index++;
  983.         }
  984.  
  985.         //now repeat for the vertexia
  986.         //Do downward pointing triangle
  987.         index = vertex_calc + 1;
  988.         for(j=1; j<=vertex_calc; j++){
  989.             pntcrd[index].theta = rotate_theta(36.0, pntcrd[j].phi, (180.0 - (atan(2) * RAD_TO_DEG)),
  990.                         pntcrd[j].theta);
  991.             pntcrd[index].phi = (rotate_phi(36.0, pntcrd[j].phi, pntcrd[j].theta,
  992.                         pntcrd[index].theta) + 36.0);
  993.             index++;
  994.         }
  995.         //Rotate triangles #1 & #2 4 times
  996.         for(i=1; i<=4; i++){
  997.             for(j=1; j<=vertex_calc * 2; j++){
  998.                   pntcrd[index].phi = pntcrd[j].phi + 72.0 * i;
  999.                   pntcrd[index].theta = pntcrd[j].theta;
  1000.                   index++;
  1001.             }
  1002.         }
  1003.         //Rotate 10 triangles by 180 degrees of theta
  1004.         for(j=1; j<=vertex_calc * 10; j++){
  1005.               pntcrd[index].phi = pntcrd[j].phi + 36.0;
  1006.               pntcrd[index].theta = 180.0 - pntcrd[j].theta;
  1007.               index++;
  1008.         }
  1009.  
  1010.     }
  1011.     else if(polytype == 2 && classtype == 1){
  1012.         //Rotate Octahedron; Class II
  1013.         //rotate chords to form hemisphere
  1014.         index = edges_calc + 1;
  1015.         for(i=1; i<=3; i++){
  1016.             for(j=1; j<=edges_calc; j++){
  1017.                   edgepts[index].sphi = edgepts[j].sphi + 90.0 * i;
  1018.                   edgepts[index].stheta = edgepts[j].stheta;
  1019.                   edgepts[index].ephi = edgepts[j].ephi + 90.0 * i;
  1020.                   edgepts[index].etheta = edgepts[j].etheta;
  1021.                   index++;
  1022.             }
  1023.         }
  1024.         //do next hemisphere
  1025.         for(i=0; i<=3; i++){
  1026.             for(j=1; j<=(edges_calc); j++){
  1027.                   edgepts[index].sphi = edgepts[j].sphi + 90.0 * i;
  1028.                   edgepts[index].stheta = 180.0 - edgepts[j].stheta;
  1029.                   edgepts[index].ephi = edgepts[j].ephi + 90.0 * i;
  1030.                   edgepts[index].etheta = 180.0 - edgepts[j].etheta;
  1031.                   index++;
  1032.             }
  1033.         }
  1034.         //rotate vertexia to form hemisphere
  1035.         index = vertex_calc + 1;
  1036.         for(i=1; i<=3; i++){
  1037.             for(j=1; j<=vertex_calc; j++){
  1038.                   pntcrd[index].phi = pntcrd[j].phi + 90.0 * i;
  1039.                   pntcrd[index].theta = pntcrd[j].theta;
  1040.                   index++;
  1041.             }
  1042.         }
  1043.         //do next hemisphere
  1044.         for(i=0; i<=3; i++){
  1045.             for(j=1; j<=(vertex_calc); j++){
  1046.                   pntcrd[index].phi = pntcrd[j].phi + 90.0 * i;
  1047.                   pntcrd[index].theta = 180.0 - pntcrd[j].theta;
  1048.                   index++;
  1049.             }
  1050.         }
  1051.  
  1052.     }
  1053.     else if(polytype == 3 && classtype == 1){
  1054.         //Rotate tetrahedron; Class I
  1055.         //find A coordinate where angles turn
  1056.         long A_change = tetra_angle();
  1057.  
  1058.         //rotate chords to form first three faces
  1059.         index = edges_calc + 1;
  1060.         for(i=1; i<=2; i++){
  1061.             for(j=1; j<=edges_calc; j++){
  1062.                   edgepts[index].sphi = edgepts[j].sphi + 120.0 * i;
  1063.                   edgepts[index].stheta = edgepts[j].stheta;
  1064.                   edgepts[index].ephi = edgepts[j].ephi + 120.0 * i;
  1065.                   edgepts[index].etheta = edgepts[j].etheta;
  1066.                   index++;
  1067.             }
  1068.         }
  1069.         //use rotation formula for bottom face chords
  1070.         for(j=1; j<=edges_calc; j++){
  1071.             edgepts[index].stheta = rotate_theta(240.0, edgepts[j].sphi, acos(-1.0/3.0) * RAD_TO_DEG,
  1072.                         edgepts[j].stheta);
  1073.             edgepts[index].sphi = rotate_phi(240.0, edgepts[j].sphi, edgepts[j].stheta,
  1074.                         edgepts[index].stheta);
  1075.             edgepts[index].etheta = rotate_theta(240.0, edgepts[j].ephi, acos(-1.0/3.0) * RAD_TO_DEG,
  1076.                         edgepts[j].etheta);
  1077.             edgepts[index].ephi = rotate_phi(240.0, edgepts[j].ephi, edgepts[j].etheta,
  1078.                         edgepts[index].etheta);
  1079.  
  1080.             if(edgepts[j].sA >= A_change)
  1081.                 edgepts[index].sphi = 180.0 - edgepts[index].sphi;
  1082.             if(edgepts[index].sphi < 0.0)
  1083.                 edgepts[index].sphi += 360.0;
  1084.             if(edgepts[j].eA >= A_change)
  1085.                 edgepts[index].ephi = 180.0 - edgepts[index].ephi;
  1086.             if(edgepts[index].ephi < 0.0)
  1087.                 edgepts[index].ephi += 360.0;
  1088.  
  1089.             index++;
  1090.         }
  1091.  
  1092.         //now do the vertexia...
  1093.         index = vertex_calc + 1;
  1094.         for(i=1; i<=2; i++){
  1095.             for(j=1; j<=vertex_calc; j++){
  1096.                   pntcrd[index].phi = pntcrd[j].phi + 120.0 * i;
  1097.                   pntcrd[index].theta = pntcrd[j].theta;
  1098.                   index++;
  1099.             }
  1100.         }
  1101.         //use rotation formula for bottom face vertexia
  1102.         for(j=1; j<=vertex_calc; j++){
  1103.             pntcrd[index].theta = rotate_theta(240.0, pntcrd[j].phi, acos(-1.0/3.0) * RAD_TO_DEG,
  1104.                         pntcrd[j].theta);
  1105.             pntcrd[index].phi = rotate_phi(240.0, pntcrd[j].phi, pntcrd[j].theta,
  1106.                         pntcrd[index].theta);
  1107.             if(pntcrd[j].A >= A_change)
  1108.                 pntcrd[index].phi = 180.0 - pntcrd[index].phi;
  1109.             if(pntcrd[index].phi < 0.0)
  1110.                 pntcrd[index].phi += 360.0;
  1111.  
  1112.             index++;
  1113.         }
  1114.     }
  1115.     else if(polytype == 1 && classtype == 2){
  1116.         //Rotate Icosahedra; Class II
  1117.         logo_display();
  1118.         cout << "Class II Sphere Not Supported --- Execution Terminating" << '\n';
  1119.         exit(1);
  1120.     }
  1121.     else if(polytype == 2 && classtype == 2){
  1122.         //Rotate ocatahedron; Class II
  1123.         logo_display();
  1124.         cout << "Class II Sphere Not Supported --- Execution Terminating" << '\n';
  1125.         exit(1);
  1126.     }
  1127.     else if(polytype == 3 && classtype == 2){
  1128.         //Rotate tetrahedron; Class II
  1129.         logo_display();
  1130.         cout << "Class II Sphere Not Supported --- Execution Terminating" << '\n';
  1131.         exit(1);
  1132.     }
  1133. }
  1134. //-------------calculate spherical coordinates for given polygon type & class
  1135. void Geodesic::spherical(void)
  1136. {
  1137.     //calculate spherical coordinates of geodesic vertexia
  1138.     //given coordinates values
  1139.     //care must be taken to avoid generating a trig error
  1140.  
  1141.     //Set the topology dependent on class type
  1142.     if(classtype == 1)
  1143.         vertex_calc = vertex;
  1144.     else if(classtype == 2)
  1145.         vertex_calc =vertexII;
  1146.  
  1147.     for(i=1; i<=vertex_calc; i++){
  1148.         //set-up x,y,z coordinates for spherical calulations
  1149.         //See page 75 of Kenner's "Geodesic Math & How to use it"
  1150.         //for formulae used below
  1151.         if(polytype == 2){
  1152.                 //Octahedron
  1153.                 pntcrd[i].X = pntcrd[i].xprime;
  1154.                 pntcrd[i].Y = pntcrd[i].yprime;
  1155.                 pntcrd[i].Z = pntcrd[i].zprime;
  1156.         }else if(polytype == 1){
  1157.                 //Icosahedron
  1158.                 pntcrd[i].X = pntcrd[i].xprime * sin(72.0 * DEG_TO_RAD);
  1159.                 pntcrd[i].Y = pntcrd[i].yprime + (pntcrd[i].xprime * cos(72.0 * DEG_TO_RAD));
  1160.                 pntcrd[i].Z = frequency/2.0 + (pntcrd[i].zprime / (2.0 * cos(36.0 * DEG_TO_RAD)));
  1161.         }else if(polytype == 3){
  1162.                 //Tetrahedron
  1163.                 pntcrd[i].X = pntcrd[i].xprime * pow(3.0, 0.5);
  1164.                 pntcrd[i].Y = 2 * pntcrd[i].yprime - pntcrd[i].xprime;
  1165.                 pntcrd[i].Z = (3.0 * pntcrd[i].zprime - pntcrd[i].xprime - pntcrd[i].yprime) / pow(2.0, 0.5);
  1166.         }
  1167.  
  1168.  
  1169.         //Calculate phi while avoiding trig errors
  1170.         if(pntcrd[i].Y == 0 && pntcrd[i].X == 0)
  1171.             pntcrd[i].phi = 0.0;
  1172.         else if(pntcrd[i].Y == 0)
  1173.             pntcrd[i].phi = 90.0;
  1174.         else
  1175.             pntcrd[i].phi = atan(pntcrd[i].X / pntcrd[i].Y) * RAD_TO_DEG;
  1176.  
  1177.         //adjust value to correct quadrant
  1178.         if(pntcrd[i].phi < 0.0)
  1179.             pntcrd[i].phi += 180.0;
  1180.  
  1181.         //now calculate theta...this is class dependent...
  1182.         if(pntcrd[i].Z == 0)
  1183.             pntcrd[i].theta = 90.0;
  1184.         else if(classtype == 1)
  1185.             //All class I types use this equation for theta
  1186.             pntcrd[i].theta = atan((pow(pow(pntcrd[i].X, 2.0) + pow(pntcrd[i].Y, 2.0), 0.5)) / pntcrd[i].Z) * RAD_TO_DEG;
  1187.         else if(polytype == 2 && classtype == 2)
  1188.             //theta for class II octahedra
  1189.             pntcrd[i].theta = atan((pow((2.0 * (pow(pntcrd[i].X, 2.0) + pow(pntcrd[i].Y, 2.0))), 0.5)) / pntcrd[i].Z) * RAD_TO_DEG;
  1190.         else if(polytype == 1 && classtype == 2)
  1191.             //theta for class II Icosahedron
  1192.             pntcrd[i].theta = atan((pow(pow(pntcrd[i].X, 2.0) + pow(pntcrd[i].Y, 2.0), 0.5)) / (cos(36.0 * DEG_TO_RAD) * pntcrd[i].Z)) * RAD_TO_DEG;
  1193.         else if(polytype == 3 && classtype ==2)
  1194.             //theta for class II tetrahedron
  1195.             //The formula given in Geodesic math (eq 12.15) appears to be inccorrect. The formula
  1196.             //below provides results indicated on tables
  1197.             pntcrd[i].theta = atan((2 * pow((pow(pntcrd[i].X, 2.0) + pow(pntcrd[i].Y, 2.0)), 0.5) / pntcrd[i].Z)) * RAD_TO_DEG;
  1198.  
  1199.         //make sure the right quadrant is used
  1200.         if(pntcrd[i].theta < 0.0)
  1201.             pntcrd[i].theta += 180.0;
  1202.     }
  1203. }
  1204. //--------------------display data function
  1205. void Geodesic::display_data(void)
  1206. {
  1207.         //Set the topology dependent on class type
  1208.         if(classtype == 1){
  1209.             vertex_calc = vertex;
  1210.             edges_calc = edges;
  1211.         }
  1212.         else if(classtype == 2){
  1213.             vertex_calc =vertexII;
  1214.             edges_calc = edgesII;
  1215.         }
  1216.         cout << "Geodesic Dome Data" << '\n';
  1217.         cout << "----------------Dome Parameters-------------------------" << '\n';
  1218.         cout << "Polyhedron Type: ";
  1219.         if(polytype == 1)
  1220.             cout << "Icosahedron" << '\n';
  1221.         else if(polytype == 2)
  1222.             cout << "Octahedron" << '\n';
  1223.         else
  1224.             cout << "Tetrahedron" << '\n';
  1225.         cout << "Class ";
  1226.         if(classtype == 1)
  1227.             cout << "I" << '\n';
  1228.         else
  1229.             cout << "II" << '\n';
  1230.         cout << "Frequency: " << frequency << '\n';
  1231.         cout << "----------------Symmetry Triangle Data------------------" << '\n';
  1232.         cout << "Vertexia: " << vertex << '\n';
  1233.         cout << "Edges: " << edges << '\n';
  1234.         cout << "Faces: " << faces << '\n' << '\n';
  1235.  
  1236. }
  1237. //--------------------End of class Geodesic
  1238.  
  1239. void help_display(void)
  1240. {
  1241.     //Display usage and help then exit
  1242.     logo_display();
  1243.     cout << "Usage: dome -fnnn -cn -px [-s] [-v] [-h] [filename.xxx]" << '\n';
  1244.     cout << "Where: -fnnn is geodesic frequency (default nnn=2)" << '\n';
  1245.     cout << "       -cn is class type (n=1 or 2; default n=1)" << '\n';
  1246.     cout << "       -px sets the polyhedron type" << '\n';
  1247.     cout << "        where x is: i for icosahedron (default)" << '\n';
  1248.     cout << "                    o for octahedron" << '\n';
  1249.     cout << "                    t for tetrahedron" << '\n';
  1250.     cout << "       -s generate full sphere data (default: symmetry triangle)" << '\n';
  1251.     cout << "       -v verbose data display at run-time" << '\n';
  1252.     cout << "       -h displays this help screen" << '\n';
  1253.     cout << "        filename.xxx is a standard DOS filename" << '\n';
  1254.     cout << "        where xxx is: DXF, DAT, POV or PRN" << '\n';
  1255.     exit(2);
  1256. }
  1257. void logo_display(void)
  1258. {
  1259.     char rev[]="$Revision: 3.50 $";
  1260.     int i, j;
  1261.     char level[6];
  1262.  
  1263.     //Get the revision level. I have been using GNU RCS to do revision control
  1264.     //on dome. This revision level will be used to automatically display the
  1265.     //correct rev level.
  1266.     j = 11;
  1267.     i = 0;
  1268.     while(rev[j] != ' '){
  1269.         level[i] = rev[j];
  1270.         j++;
  1271.         i++;
  1272.     }
  1273.     level[i] = '\0';
  1274.  
  1275.     #ifdef __BORLANDC__
  1276.     clrscr();
  1277.     #endif
  1278.  
  1279.     cout << "Dome " << level << ", Copyright (C) 1995, Richard J. Bono" << '\n';
  1280.     cout << "Dome comes with ABSOLUTELY NO WARRANTY. This is free software," << '\n';
  1281.     cout << "and you are welcome to redistribute it under certain conditions." << '\n';
  1282.     cout << "See GNU General Public License for more details." << '\n' << '\n';
  1283. }
  1284.  
  1285. void get_cmd(struct parameters *command, int param_count, char *param[])
  1286. {
  1287.     //Get and parse command line
  1288.     char cmd_parm[6];
  1289.  
  1290.     //Set defaults
  1291.     command->freq = 2;            //frequency = 2
  1292.     command->classt = 1;        //Class I
  1293.     command->polyt = 1;            //Icosahedron
  1294.     command->verbose_flag = 0;  //disable verbose
  1295.     command->filet = 5;            //No file output
  1296.     command->sphere_flag = 0;    //Symmetry triangle only
  1297.  
  1298.     int t, j, k;
  1299.  
  1300.     for(t=1; t<param_count; ++t){
  1301.         if(param[t][0] == '-'){
  1302.             switch (tolower(param[t][1])){
  1303.                 case ('p'):
  1304.                     //Set polyhedron type
  1305.                     if(tolower(param[t][2]) == 'i')
  1306.                         //Set to Icosahedron
  1307.                         command->polyt = 1;
  1308.                     else if(tolower(param[t][2]) == 'o')
  1309.                         //Set to octahedron
  1310.                         command->polyt = 2;
  1311.                     else if(tolower(param[t][2]) == 't')
  1312.                         //Set to tetrahedron
  1313.                         command->polyt = 3;
  1314.                     else{
  1315.                         logo_display();
  1316.                         cout << "Invalid Polyhedron Type --- Execution Terminating" << '\n';
  1317.                         exit(1);
  1318.                     }
  1319.                     break;
  1320.                 case ('h'):
  1321.                     //Display help and exit. This overrides other parameters
  1322.                     help_display();
  1323.                     break;
  1324.                 case ('v'):
  1325.                     //Enable Parameter Display during execution
  1326.                     command->verbose_flag = 1;
  1327.                     break;
  1328.                 case ('s'):
  1329.                     //generate sphere
  1330.                     command->sphere_flag = 1;
  1331.                     break;
  1332.                 case ('f'):
  1333.                     //get frequency
  1334.                     j=2;
  1335.                     k=0;
  1336.                     while(param[t][j]){
  1337.                         cmd_parm[k] = param[t][j];
  1338.                         j++;
  1339.                         k++;
  1340.                     }
  1341.                     cmd_parm[k] = '\0';
  1342.                     command->freq = atol(cmd_parm);
  1343.                     if (command->freq <= 0){
  1344.                         logo_display();
  1345.                         cout << "Invalid Frequency --- Execution Terminating" << '\n';
  1346.                         exit(1);
  1347.                     }
  1348.                     break;
  1349.                 case ('c'):
  1350.                     //get class type
  1351.                     j=2;
  1352.                     k=0;
  1353.                     while(param[t][j]){
  1354.                         cmd_parm[k] = param[t][j];
  1355.                         j++;
  1356.                         k++;
  1357.                     }
  1358.                     cmd_parm[k] = '\0';
  1359.                     command->classt = atol(cmd_parm);
  1360.                     if (command->classt < 1 || command->classt > 2){
  1361.                         logo_display();
  1362.                         cout << "Invalid Class Type --- Execution Terminating" << '\n';
  1363.                         exit(1);
  1364.                     }
  1365.                     break;
  1366.                 default:
  1367.                     logo_display();
  1368.                     cout << "Invalid command-line --- Execution Terminating" << '\n';
  1369.                     exit(1);
  1370.                     break;
  1371.             }
  1372.         }
  1373.         else{
  1374.             //Check to see if parameter is a valid filename
  1375.             j = 0;
  1376.             while(param[t][j]){
  1377.                 if(param[t][j] != '.')
  1378.                     j++;
  1379.                 else{
  1380.                     //Check for valid extension
  1381.                     j++;
  1382.                     k = 0;
  1383.                     while(param[t][j]){
  1384.                         cmd_parm[k] = tolower(param[t][j]);
  1385.                         j++;
  1386.                         k++;
  1387.                     }
  1388.                     cmd_parm[k] = '\0';
  1389.                     if(!strcmp(cmd_parm,"dxf"))
  1390.                         command->filet = 1;
  1391.                     else if(!strcmp(cmd_parm, "dat"))
  1392.                         command->filet = 2;
  1393.                     else if(!strcmp(cmd_parm, "pov"))
  1394.                         command->filet = 3;
  1395.                     else if(!strcmp(cmd_parm, "prn"))
  1396.                         command->filet = 4;
  1397.                     else{
  1398.                         logo_display();
  1399.                         cout << "Invalid command-line --- Execution terminating" << '\n';
  1400.                         exit(1);
  1401.                     }
  1402.                     j = 0;
  1403.                     k = 0;
  1404.                     while(param[t][j]){
  1405.                         command->filename[k] = tolower(param[t][j]);
  1406.                         j++;
  1407.                         k++;
  1408.                     }
  1409.                     command->filename[k] = '\0';
  1410.                 }
  1411.             }
  1412.         }
  1413.     }
  1414.     if(fmod(command->freq, 2) != 0 && command->classt == 2){
  1415.         logo_display();
  1416.         cout << "Class II requires Even Frequency --- Execution Terminating" << '\n';
  1417.         exit(1);
  1418.     }
  1419. }
  1420.  
  1421. int main(int argc, char *argv[])
  1422. {
  1423.     //This main routines shows what can be done with this class. It implements
  1424.     //a simple program which displays dome parameters and optionally saves the
  1425.     //data in either DXF or ASCII file formats.
  1426.  
  1427.     //Some things that can be tried involve creating several instances of the
  1428.     //geodesic object. Geodesic shells can be created in this way given enough
  1429.     //memory.
  1430.  
  1431.     //Command line parameters are changing this routine into something more
  1432.     //permanent. Remember the class can be incorporated into your own programs.
  1433.     //This shell is just one possible implementation.
  1434.  
  1435.     //Get dome parameters
  1436.     if(argc == 1)
  1437.         //display usage and exit
  1438.         help_display();
  1439.     else
  1440.         //Get command line
  1441.         get_cmd(&cmd_parm, argc, argv);
  1442.  
  1443.     //class instance
  1444.     Geodesic geosys(&cmd_parm);
  1445.  
  1446.     logo_display();
  1447.     if(cmd_parm.verbose_flag)
  1448.         geosys.display_data();
  1449.  
  1450.     if(cmd_parm.filet == 1)
  1451.         //output file in DXF format
  1452.         geosys.save_dxf(cmd_parm.filename);
  1453.     else if(cmd_parm.filet == 2)
  1454.         //output all data in ASCII report format
  1455.         geosys.save_ascii(cmd_parm.filename);
  1456.     else if(cmd_parm.filet ==3)
  1457.         //output data in POV-Ray script format
  1458.         geosys.save_POV(cmd_parm.filename);
  1459.     else if(cmd_parm.filet ==4)
  1460.         //output data in PRN format
  1461.         geosys.save_PRN(cmd_parm.filename);
  1462.  
  1463.     cout << '\n' << "Execution Complete!" << '\n';
  1464.     return(0);
  1465. }
  1466.