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