1 // StarPlot - A program for interactively viewing 3D maps of stellar positions.
2 // Copyright (C) 2000  Kevin B. McCarty
3 //
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 2
7 // of the License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17 
18 
19 //
20 // star.cc - The Star class.
21 //
22 
23 #include "star.h"
24 #include "greek.h"
25 
26 #define NEED_FULL_NAMES
27 #include "constellations.h"
28 
29 using std::string;
30 
31 // For the Star class ------------------------------------------
32 
33 // Private function
34 
35 // Function to set a bunch of star data to save typing in the copy constructor
36 //  and operator= functions.
Obtain(StringList Names,StringList Membership,StringList Comments,SolidAngle GridPosn,double Distance,double Diameter,double PrimaryDistance,double Magnitude,SpecClass Spectrum,size_t Place,unsigned int XPixel,unsigned int YPixel,unsigned int RPixel,bool SLabelDraw)37 void Star::Obtain(StringList Names, StringList Membership, StringList Comments,
38 		  SolidAngle GridPosn, double Distance, double Diameter,
39 		  double PrimaryDistance, double Magnitude, SpecClass Spectrum,
40 		  size_t Place, unsigned int XPixel, unsigned int YPixel,
41 		  unsigned int RPixel, bool SLabelDraw)
42 {
43   sNames = Names, sMembership = Membership, sComments = Comments;
44   sGridPosn = GridPosn, sDistance = Distance, sDiameter = Diameter;
45   sPrimaryDistance = PrimaryDistance, sMagnitude = Magnitude;
46   sSpectrum = Spectrum, sPlace = Place, xPixel = XPixel, yPixel = YPixel;
47   rPixel = RPixel; sLabelDraw = SLabelDraw;
48   return;
49 }
50 
51 
52 // Public functions
53 
54 // Default constructor
Star()55 Star::Star()
56 {
57   Obtain(StringList(), StringList(), StringList(), SolidAngle(0,0),
58 	 0.0, 0.0, 0.0, 0.0, SpecClass(), 0, 0, 0, 0, true);
59 }
60 
61 // Copy constructor
Star(const Star & s)62 Star::Star(const Star &s)
63 {
64   Obtain(s.sNames, s.sMembership, s.sComments, s.sGridPosn, s.sDistance,
65 	 s.sDiameter, s.sPrimaryDistance, s.sMagnitude, s.sSpectrum,
66 	 s.sPlace, s.xPixel, s.yPixel, s.rPixel, s.sLabelDraw);
67 }
68 
69 // Assignment operator
operator =(const Star & s)70 Star & Star::operator = (const Star &s)
71 {
72   if (! (this == &s)) { // no point in copying if objects already the same
73     Obtain(s.sNames, s.sMembership, s.sComments, s.sGridPosn, s.sDistance,
74 	   s.sDiameter, s.sPrimaryDistance, s.sMagnitude, s.sSpectrum,
75 	   s.sPlace, s.xPixel, s.yPixel, s.rPixel, s.sLabelDraw);
76   }
77   return *this;
78 }
79 
80 
81 // Constructor to create a Star from a formatted text record.  Text record
82 //  is assumed to be in the following format.  Line breaks are permitted,
83 //  but only after the semicolon ending a field.
84 //
85 //   StarName1,StarName2,...;RA(h,m,s);Dec(d,m,s);Distance(L-Y);Diameter(L-Y);
86 //   SpectralClass;Magnitude;PrimaryDistance(L-Y);
87 //   Membership;Comments
88 //
89 //  Example (Proxima Centauri):
90 //
91 //   Proxima Cen,Alpha Cen C,Rigil Kent C;14,32,0;-62,49,0;4.24;0;M5e V;
92 //   15.49;0.252;Alpha Cen triple system;Nearest star, known flare star [etc]
93 //
94 //  (The diameter field should be zero in the record if not specifically
95 //  known; it will then be calculated from the spectral class and magnitude.)
96 //
97 //  Note: Other field and subfield separators than ';' and ',' may be assumed
98 //  by changing the definitions of FIELD_DELIMITER and SUBFIELD_DELIMITER
99 //  in "star.h".
100 //
101 //  The ctor "fastconversion" argument, if set to true, will cause only the
102 //  most fundamental attributes about the star (position and basic
103 //  spectral class) to be written.  This is for speed.  The default is false.
104 //
105 //  The "nameconvert" argument, if true, causes any constellation abbreviations
106 //  in the name, e.g. "UMa", to be translated into the full genitive form,
107 //  e.g. "Ursae Majoris".
108 
109 #define FIELDS(n) ((fields.size() > (n)) ? fields[(n)] : string(""))
110 
Star(const string & record,bool fastconversion,bool nameconvert)111 Star::Star(const string &record, bool fastconversion, bool nameconvert)
112 {
113   // First, tokenize record into fields
114   StringList fields(record, FIELD_DELIMITER);
115   fields.stripspace();
116   double RA, Dec;
117 
118   // Now, extract data from each field.  First the fields for a fast
119   //  conversion (where all we want is to see if the star passes the filters):
120 
121   // Right ascension and declination: convert from h,m,s and
122   //  +/-d,m,s to decimal format, then put into SolidAngle sGridPosn
123   RA = starstrings::str_to_ra(FIELDS(1), SUBFIELD_DELIMITER);
124   Dec = starstrings::str_to_dec(FIELDS(2), SUBFIELD_DELIMITER);
125   sGridPosn = SolidAngle(RA, Dec);
126 
127   // Other fields
128   sDistance = starmath::atof(FIELDS(3));
129   sSpectrum = SpecClass(FIELDS(5));
130 
131   if (fastconversion) /* then bail out here */
132     return;
133 
134   // Then everything else:
135 
136   sSpectrum.initialize();
137   sMagnitude = starmath::atof(FIELDS(6));
138 
139   sNames = StringList(FIELDS(0), SUBFIELD_DELIMITER);
140   sNames.stripspace();
141   sNames.utf8ize();
142 
143   // translate constellation abbrevs. to genitive names
144   if (nameconvert) {
145     int constel = -1; // cache constellation name once known
146     iterate (StringList, sNames, name_ptr) {
147       if (constel < 0) {
148         for (int i = 0; i < NUM_CONSTELLATIONS; i++) {
149 	  if (starstrings::find_and_replace(*name_ptr,
150 	      string(" ") + constellations[i], string(" ") + constelnames[i])) {
151 	    constel = i;
152 	    goto next_name;
153 	  }
154 	}
155       }
156       else { // constel is known
157 	starstrings::find_and_replace(*name_ptr,
158 				      string(" ") + constellations[constel],
159 				      string(" ") + constelnames[constel]);
160       }
161       next_name: continue;
162     }
163   }
164 
165   sDiameter = starmath::atof(FIELDS(4));
166   if (sDiameter <= 0.0)
167     sDiameter = sSpectrum.diameter(sMagnitude);
168 
169   sPrimaryDistance = starmath::atof(FIELDS(7));
170   sMembership = StringList(FIELDS(8), SUBFIELD_DELIMITER);
171   sMembership.stripspace();
172   sMembership.utf8ize();
173   sComments.push_back(FIELDS(9));
174   sComments.stripspace();
175   sComments.utf8ize();
176 
177   sPlace = xPixel = yPixel = rPixel = 0;
178 }
179 
180 
181 // function to determine whether the star should be included in a StarArray
182 //  with the set of rules given in "rules".
183 //
184 //  Note: If star is a secondary star "too close" to its primary, it should
185 //  be be put into the array ONLY if it passes the filter but its primary
186 //  doesn't.  This filtering occurs in StarArray::Read(), NOT here.
187 
PassesFilter(const Rules & rules) const188 bool Star::PassesFilter(const Rules &rules) const
189 {
190   // Filtering by magnitude is already done by the first filter in
191   //  StarArray::Read().
192 
193   // Filter by location
194   Vector3 relativeLocation = GetStarXYZ() - rules.ChartLocation;
195   if (relativeLocation.magnitude() > rules.ChartRadius)
196     return false;
197 
198   // Filter by spectral class
199   if (! rules.StarClasses[SpecHash(GetStarClass().classmajor())])
200     return false;
201 
202   // if we get down here, star passed all the filters.
203   return true;
204 }
205 
206 
207 // Display(): plot the star onto the painting device, as viewed from a
208 //  specified point and orientation.  (We assume that the star coordinates
209 //  and chart center given are in the same coordinate system.)
210 //
211 //  Note: StarViewer is a generic display class defined in viewer.h -- I am
212 //  trying to keep the graphics-library dependent functions contained in
213 //  descendant classes of StarViewer (e.g. KDEViewer, GTKViewer, etc.,
214 //  which will be wrapper classes around the given graphics libraries).
215 //  This will make the code a bit more confusing, but make the program
216 //  easier to port.
217 
Display(const Rules & rules,StarViewer * sv) const218 void Star::Display(const Rules &rules, StarViewer *sv) const
219 {
220   int wincenterX, wincenterY, starBaseY;
221   unsigned int windowsize, pixelradius;
222 
223   Vector3 relativeLocation;
224   double x, y, z;
225   color_t barcolor;
226 
227   Vector3 center = rules.ChartLocation;
228   double radius = rules.ChartRadius;
229   SolidAngle orientation = rules.ChartOrientation;
230   bool bar = rules.StarBars;
231 
232   // Determine radius and center of chart, in pixels
233   windowsize = (sv->width() > sv->height()) ? sv->height() : sv->width();
234   pixelradius = ROUND(0.4 * windowsize);
235   wincenterX = sv->width() / 2;
236   wincenterY = sv->height() / 2;
237 
238   // Determine star position in "local coordinates", where:
239   //  XZ-plane is vertical and perpendicular to the computer screen,
240   //   with X-axis pointing out of the screen and tipped DOWNward
241   //   by the angle orientation.getTheta()
242   //  Y-axis is horizontal, parallel to the screen, and pointing rightward
243   //  Distances are in pixels
244 
245   relativeLocation = GetStarXYZ() - center;
246   x = relativeLocation.getX() * cos(orientation.getPhi())
247     + relativeLocation.getY() * sin(orientation.getPhi());
248   y = -relativeLocation.getX() * sin(orientation.getPhi())
249     + relativeLocation.getY() * cos(orientation.getPhi());
250   z = relativeLocation.getZ();
251   relativeLocation = Vector3(x,y,z) * pixelradius / radius;
252 
253   // Determine 2-D projection of this relative location onto the screen.
254   xPixel = wincenterX + ROUND(relativeLocation.getY());
255   starBaseY = wincenterY
256     + ROUND((relativeLocation.getX() * sin(orientation.getTheta())));
257   yPixel = starBaseY
258     - ROUND((relativeLocation.getZ() * cos(orientation.getTheta())));
259 
260   // Determine how large the star should be drawn.
261   if (sDiameter * pixelradius / (2 * radius) > STAR_PIXEL_RADIUS) {
262     rPixel = ROUND(sDiameter * pixelradius / (2 * radius));
263     if (rPixel > pixelradius) rPixel = pixelradius;
264   }
265   else
266     rPixel = 0;
267 
268   // Draw the star and accompanying devices (position bar, label).
269 
270   barcolor = (relativeLocation.getZ() >= 0.0) ? POSITIVE : NEGATIVE;
271 
272   if (bar) {
273     if (relativeLocation.getZ() * orientation.getTheta() >= 0.0) {
274       // then star is in the hemisphere of the chart facing us, so:
275       // draw the reference ellipse on the x-y plane
276       sv->setcolor(barcolor);
277       sv->setfill(false);
278       sv->drawellipse(xPixel, starBaseY, 3, 2);
279       // draw the vertical bar
280       sv->setcolor(BACKGROUND);
281       sv->drawline(xPixel - 1, starBaseY, xPixel - 1, yPixel);
282       sv->drawline(xPixel + 1, starBaseY, xPixel + 1, yPixel);
283       sv->setcolor(barcolor);
284       sv->drawline(xPixel, starBaseY, xPixel, yPixel);
285     }
286 
287     else {
288       // outline the vertical bar
289       sv->setcolor(BACKGROUND);
290       sv->drawline(xPixel - 1, starBaseY, xPixel - 1, yPixel);
291       sv->drawline(xPixel + 1, starBaseY, xPixel + 1, yPixel);
292     }
293   }
294 
295   // draw the star and label
296   rPixel = Draw(rules, sv, xPixel, yPixel, rPixel);
297   // make sure the effective mouse-click radius isn't too small:
298   if (rPixel < STAR_PIXEL_RADIUS) rPixel = STAR_PIXEL_RADIUS;
299 
300   if (bar && relativeLocation.getZ() * orientation.getTheta() < 0.0) {
301     // draw the vertical bar
302     sv->setcolor(barcolor);
303     sv->drawline(xPixel, starBaseY, xPixel, yPixel);
304     // draw the ellipse
305     sv->setcolor(barcolor);
306     sv->setfill(false);
307     sv->drawellipse(xPixel, starBaseY, 3, 2);
308   }
309 
310   return;
311 }
312 
313 
314 // Draw(): plot the star onto the painting device at a given pixel location,
315 //  not worrying about coordinates, etc.  Returns the radius of circle drawn,
316 //  in pixels.
317 
Draw(const Rules & rules,StarViewer * sv,int xposn,int yposn,int pixelradius) const318 unsigned int Star::Draw(const Rules &rules, StarViewer *sv,
319 			int xposn, int yposn, int pixelradius) const
320 {
321   // set size of star to pixelradius, unless pixelradius is <= 0 / not given:
322 
323   if (pixelradius <= 0) {
324     pixelradius = STAR_PIXEL_RADIUS;
325 
326     if (rules.StarDiameters == MK_DIAMETERS) {
327       // Increase size of the circle for giant-class stars.
328       double mktype = sSpectrum.MKtype();
329       if (mktype > 0.0) {
330 	if (mktype <= 3.5)
331 	  pixelradius++;
332 	if (mktype <= 1.5)
333 	  pixelradius++;
334       }
335     }
336 
337     else if (rules.StarDiameters == MAGNITUDE_DIAMETERS) {
338       // Set size of the circle based upon the absolute magnitude.
339       if (sMagnitude >= 8.0) pixelradius = 1;
340       else if (sMagnitude < -4.0) pixelradius = 5;
341       else pixelradius = ROUND(4.0 - sMagnitude / 4.0);
342     }
343   }
344 
345   // Draw the circle
346   sv->setfill(true);
347   sv->setcolor(sSpectrum.color());
348   sv->drawstar(xposn, yposn, pixelradius);
349 
350   if (rules.StarLabels != NO_LABEL) { // number the star appropriately
351     string starPlace;
352     star_label_t label = rules.StarLabels;
353 
354     // make non-stellar object labels a different color
355     sv->setcolor((sSpectrum.classmajor() == '*') ? NON_STELLAR_COLOR :
356 		 TEXT_COLOR);
357 
358     sv->setfill(false);
359 
360     switch (label) {
361     case NUMBER_LABEL:
362       starPlace = starstrings::itoa(sPlace);
363       sv->drawtext(starPlace,
364 		   xposn + ROUND(pixelradius / M_SQRT2) + 3,
365 		   yposn - ROUND(pixelradius / M_SQRT2) - 3);
366       break;
367     case LANDMARK_LABEL:
368       if (!sLabelDraw && sNames[0] != string("Sun"))
369 	break;
370       // else continue to the next label:
371     case STRING_LABEL:
372       sv->drawtext(sNames[0],
373 		   xposn + ROUND(pixelradius / M_SQRT2) + 3,
374 		   yposn - ROUND(pixelradius / M_SQRT2) - 3);
375       break;
376     default: // do nothing
377       break;
378     }
379   }
380 
381   return pixelradius;
382 }
383 
384 
385 // function to take star information, format it properly, and append it
386 //  as strings to a StringList.
387 
GetInfo(const Rules & rules,bool punctuation,char sep) const388 StringList Star::GetInfo(const Rules &rules, bool punctuation,
389 		         char sep) const
390 {
391   StringList output;
392   output.push_back(starstrings::itoa(sPlace));		// 0. Label number
393 
394   // 1. Most common designation
395   output.push_back((sNames.size()) ? sNames[0] : "");
396 
397   if (sDistance == 0.0) {
398     /* TRANSLATORS: This is an abbreviation for "Not Applicable". */
399     output.push_back(_("N/A"));
400     output.push_back(_("N/A"));
401   }
402   else {
403     // 2. { Right ascension | Galactic longitude }
404     output.push_back(starstrings::ra_to_str(sGridPosn.getPhi(), sep,
405 					    rules.CelestialCoords,
406 					    punctuation));
407     // 3. { Declination | Galactic latitude }
408     output.push_back(starstrings::dec_to_str(sGridPosn.getTheta(), sep,
409 					     punctuation));
410   }
411 
412   output.push_back(starstrings::ftoa(sDistance, 5));	// 4. Distance in L-Y
413   output.push_back(sSpectrum.print());			// 5. Spectral class
414   output.push_back(starstrings::ftoa(sMagnitude, 4));	// 6. M_v
415   if (sMembership.size())
416     output.push_back(sMembership[0]);			// 7. Cluster membership
417   else
418     output.push_back("");
419   if (sComments.size())
420     output.push_back(sComments[0]);			// 8. Comments
421   else
422     output.push_back("");
423   output.push_back(starstrings::ftoa(sPrimaryDistance, 5));
424 						// 9. Distance from primary
425   citerate_from (StringList, sNames, i, 1)
426     output.push_back(*i);			// 10+. Other names for star
427 
428   return output;
429 }
430 
431