#include "strings.h" #include "greek.h" #include "mathdefs.h" #include #include #include #include #include #include using std::string; using std::vector; using std::ostringstream; // Note: if the character to tokenize is repeated, null strings will // not be created. E.g., "safs;;bq;g" with tokenizer ';' will result // in three strings, not four. To avoid this problem, data files // should always use a space to represent a null argument: "safs; ;bq;g" StringList::StringList(const std::string &s, char tokenizer) : vector(), c_strings(0), c_strings_size(0) { size_t posn = 0, newposn = 0; while (newposn < s.size()) { posn = s.find_first_not_of(tokenizer, newposn); newposn = s.find(tokenizer, posn); if (posn < s.size()) push_back(s.substr(posn, newposn - posn)); } } string StringList::flatten(char spacer) const { string result; citerate_until (StringList, *this, i, -1) result += (*i + spacer); result += *(end() - 1); return result; } char ** StringList::c_str() const { delstrings(); c_strings_size = size(); c_strings = new char * [c_strings_size]; citerate (StringList, *this, ptr) { size_t i = ptr - begin(); c_strings[i] = new char [(*ptr).size() + 1]; std::strcpy(c_strings[i], (*ptr).c_str()); } return c_strings; } // This function is to fix the apparent lack of a zero-padding command // in (setfill() doesn't seem to do anything)... static string pad(int width, double d, char c) { int chars = (d >= 10.0) ? width - FLOOR(std::log10(d)) - 1 : width - 1; return (chars > 0) ? std::string(chars, c) : ""; } // Right now this function just looks for the degree symbol \xB0, checks // to make sure it's in UTF-8 form \xC2\xB0, and if not makes it so. // XXX: This should obviously be much more general. void starstrings::utf8ize(string &s) { const static unsigned char deg_iso8859 = 0xB0; const static unsigned char deg_utf8_prefix = 0xC2; string::size_type posn = s.find(deg_iso8859); for ( ; posn < s.size(); posn = s.find(deg_iso8859, posn + 1)) if (!posn || posn < s.size() && static_cast(s[posn - 1]) < 0x80) s.insert(posn++, 1, deg_utf8_prefix); } // Convert "alpha" to the UTF-8 character for alpha, etc. void starstrings::addgreek(string &s) { int i = 1; while (i < NUM_GREEK_LETTERS && ! compare_n(s, Greek[i].name, Greek[i].name.size())) i++; if (i < NUM_GREEK_LETTERS) s = string(Greek[i].utf8) + s.substr(Greek[i].name.size()); } string starstrings::ftoa(double d, int precision, int width, bool zeropadding) { // Which will take more characters: N.NNNNe+MM or NNNNN000 ? // First form uses precision + 1[.] + 4[e+MM] = precision + 5 characters // Second form uses n := MM + 1 characters // Hence if precision < n <= precision + 5 we should force the // non-exponential form by setting precision := n double mag = std::fabs(d); if (mag >= 1) { // # of digits left of decimal point int n_digits = 1 + FLOOR(std::log10(mag)); if (n_digits > precision && n_digits <= precision + 5) precision = n_digits; } ostringstream o; o.precision(precision); if (width) { if (zeropadding) o << pad(width, std::fabs(d), '0'); else o << pad(width, std::fabs(d), ' '); } o << starmath::sigdigits(d, precision); return o.str(); } string starstrings::ltoa(long int i, int width, bool zeropadding) { ostringstream o; if (width) { if (zeropadding) o << pad(width, std::labs(i), '0'); else o << std::setw(width); } o << i; return o.str(); } // It would be nice to write this function such that it could take std::string // as a printable argument. Unfortunately there seems no way to do this // outside of GNU C's non-portable register_printf_function(). string starstrings::ssprintf(const char * format, ...) { // It's not clear whether or not vsprintf() and vsnprintf() are supposed // to be in namespace std or in the global namespace. Hedge our bets: using namespace std; char test[2], * buffer; va_list args; // Figure out how much space is needed. Supposedly the printf() family // return the number of bytes that would have been written even if not // all of them are. Hope this is true... va_start(args, format); size_t size = vsnprintf(test, 2, format, args); va_end(args); // Write the result to a string buffer = new char[size + 1]; va_start(args, format); vsprintf(buffer, format, args); string result(buffer); // clean up va_end(args); delete [] buffer; return result; } StringList starstrings::dec_to_strs(double dec, bool symbols) { int deg, min; double sec; StringList result; result.reserve(3); dec /= RAD_PER_DEGREE; result.push_back((dec < 0) ? "-" : (dec > 0) ? "+" : " "); if (dec < 0) dec *= -1; deg = FLOOR(dec); min = FLOOR(FRAC(dec) * 60); sec = starmath::roundoff((FRAC(dec) * 60 - min) * 60, 3); // test to make sure we don't have min or sec == 60 string sec_str = starstrings::ftoa(sec, 4, 2, true); if (sec_str == string("60")) { sec_str = "00"; min++; } if (min == 60) { min = 0; deg++; } if (deg >= 90) { deg = 90; min = 0; sec_str = "00"; } result[0] += starstrings::itoa(deg, 2, true); result.push_back(starstrings::itoa(min, 2, true)); result.push_back(sec_str); if (symbols) { result[0] += DEGREE_UTF8; result[1] += "'"; result[2] += '"'; } return result; } StringList starstrings::ra_to_strs(double ra, bool celestial_coords, bool symbols) { int h, m; double s; StringList result; result.reserve(3); while (ra < 0) ra += 2 * M_PI; ra /= (celestial_coords ? RAD_PER_HOUR : RAD_PER_DEGREE); h = FLOOR(ra); m = FLOOR(FRAC(ra) * 60); s = starmath::roundoff((FRAC(ra) * 60 - m) * 60, 3); // test to make sure we don't have m or s == 60 string s_str = starstrings::ftoa(s, 4, 2, true); if (s_str == string("60")) { s_str = "00"; m++; } if (m == 60) { m = 0; h++; } if (h >= 24 && celestial_coords) { h %= 24; } if (h >= 360 && ! celestial_coords) { h %= 360; } result.push_back(starstrings::itoa(h, celestial_coords ? 2 : 3, true)); result.push_back(starstrings::itoa(m, 2, true)); result.push_back(s_str); if (symbols) { result[0] += (celestial_coords ? /* TRANSLATORS: This is the abbreviation for "hours of right ascension". */ _("h") : DEGREE_UTF8); result[1] += (celestial_coords ? /* TRANSLATORS: This is the abbreviation for "minutes of right ascension". */ _("m") : "'"); result[2] += (celestial_coords ? /* TRANSLATORS: This is the abbreviation for "seconds of right ascension". */ _("s") : "\""); } return result; } double starstrings::strs_to_dec(const StringList &decstrings) { if (! decstrings.size()) return 0; double result = std::fabs(starmath::atof(decstrings[0])); if (decstrings.size() > 1) result += starmath::atof(decstrings[1]) / 60.0; if (decstrings.size() > 2) result += starmath::atof(decstrings[2]) / 3600.0; if (decstrings[0].find('-') < decstrings[0].size()) result *= -1; return result * RAD_PER_DEGREE; } double starstrings::strs_to_ra(const StringList &rastrings, bool celestial_coords) { if (! rastrings.size()) return 0; double result = starmath::atof(rastrings[0]); if (rastrings.size() > 1) result += starmath::atof(rastrings[1]) / 60.0; if (rastrings.size() > 2) result += starmath::atof(rastrings[2]) / 3600.0; return result * (celestial_coords ? RAD_PER_HOUR : RAD_PER_DEGREE); } double starstrings::strs_to_dec(const string &d, const string &m, const string &s) { StringList temp; temp.push_back(d); temp.push_back(m); temp.push_back(s); return strs_to_dec(temp); } double starstrings::strs_to_ra(const string &h, const string &m, const string &s, bool celestial_coords) { StringList temp; temp.push_back(h); temp.push_back(m); temp.push_back(s); return strs_to_ra(temp, celestial_coords); } // Distance units: in order, LY, pc, kpc, AU, km, mi. double distance_conversion[N_DIST] = { 1.0, 1.0/LY_PER_PARSEC, 0.001/LY_PER_PARSEC, AU_PER_LY, KM_PER_LY, MILE_PER_LY }; const char * distance_cname[N_DIST] = { "LY","pc","kpc","AU","km","miles" }; const char * distance_name[N_DIST] = { /* Translators: LY is the abbreviation for "light-year" */ _("LY"), "pc", "kpc", /* Translators: AU is the abbreviation for "astronomical unit" */ _("AU"), "km", _("miles") }; distance_unit starstrings::str_to_unit(const string & s) { for (unsigned i = 0; i < N_DIST; i++) if (string(distance_cname[i]) == s) return static_cast(i); // if we got here, string corresponds to invalid unit; assume the default return static_cast(0); } std::string starstrings::distance_to_str(double distance_ly, distance_unit unit) { return starstrings::ftoa(distance_ly * distance_conversion[unit], 5) + " " + distance_name[unit]; } // "unit" pointer is to an array of 4 distance_unit enums, containing in order // the desired large-scale, medium-scale, small-scale and very-small-scale // units. std::string starstrings::distance_to_str(double distance_ly, const distance_unit * unit) { const static distance_unit default_units[4] = { DIST_LY, DIST_LY, DIST_AU, DIST_KM }; if (! unit) unit = default_units; double mag = std::fabs(distance_ly); if (mag * distance_conversion[unit[1]] >= 1000.) return distance_to_str(distance_ly, unit[0]); else if (mag == 0 || mag * distance_conversion[unit[1]] >= 0.01) return distance_to_str(distance_ly, unit[1]); else if (mag * distance_conversion[unit[2]] >= 0.01) return distance_to_str(distance_ly, unit[2]); else return distance_to_str(distance_ly, unit[3]); }