3 // Part of the Architektonas Project
4 // Originally part of QCad Community Edition by Andrew Mustun
5 // Extensively rewritten and refactored by James L. Hammons
6 // Portions copyright (C) 2001-2003 RibbonSoft
7 // Copyright (C) 2010 Underground Software
8 // See the README and GPLv2 files for licensing and warranty information
10 // JLH = James L. Hammons <jlhamm@acm.org>
13 // --- ---------- -----------------------------------------------------------
14 // JLH 05/05/2010 Moved implementation from header to this file. :-)
24 * Converts a DXF integer () to a Unit enum.
26 RS2::Unit RS_Units::dxfint2unit(int dxfint)
28 return (RS2::Unit)dxfint;
41 return RS2::Millimeter;
43 return RS2::Centimeter;
47 return RS2::Kilometer;
49 return RS2::Microinch;
57 return RS2::Nanometer;
61 return RS2::Decimeter;
63 return RS2::Decameter;
65 return RS2::Hectometer;
67 return RS2::Gigameter;
71 return RS2::Lightyear;
78 * @return a short string representing the given unit (e.g. "mm")
80 QString RS_Units::unitToSign(RS2::Unit u)
101 case RS2::Centimeter:
134 case RS2::Hectometer:
159 * @return a string representing the given unit (e.g. "Millimeter").
160 * translated if @a t is true (the default).
162 QString RS_Units::unitToString(RS2::Unit u, bool t)
169 ret = t ? QObject::tr("None") : QString("None");
172 ret = t ? QObject::tr("Inch") : QString("Inch");
175 ret = t ? QObject::tr("Foot") : QString("Foot");
178 ret = t ? QObject::tr("Mile") : QString("Mile");
180 case RS2::Millimeter:
181 ret = t ? QObject::tr("Millimeter") : QString("Millimeter");
183 case RS2::Centimeter:
184 ret = t ? QObject::tr("Centimeter") : QString("Centimeter");
187 ret = t ? QObject::tr("Meter") : QString("Meter");
190 ret = t ? QObject::tr("Kilometer") : QString("Kilometer");
193 ret = t ? QObject::tr("Microinch") : QString("Microinch");
196 ret = t ? QObject::tr("Mil") : QString("Mil");
199 ret = t ? QObject::tr("Yard") : QString("Yard");
202 ret = t ? QObject::tr("Angstrom") : QString("Angstrom");
205 ret = t ? QObject::tr("Nanometer") : QString("Nanometer");
208 ret = t ? QObject::tr("Micron") : QString("Micron");
211 ret = t ? QObject::tr("Decimeter") : QString("Decimeter");
214 ret = t ? QObject::tr("Decameter") : QString("Decameter");
216 case RS2::Hectometer:
217 ret = t ? QObject::tr("Hectometer") : QString("Hectometer");
220 ret = t ? QObject::tr("Gigameter") : QString("Gigameter");
223 ret = t ? QObject::tr("Astro") : QString("Astro");
226 ret = t ? QObject::tr("Lightyear") : QString("Lightyear");
229 ret = t ? QObject::tr("Parsec") : QString("Parsec");
241 * Converts a string into a unit enum.
243 RS2::Unit RS_Units::stringToUnit(const QString & u)
245 RS2::Unit ret = RS2::None;
251 else if (u == QObject::tr("Inch"))
255 else if (u==QObject::tr("Foot"))
259 else if (u==QObject::tr("Mile"))
263 else if (u==QObject::tr("Millimeter"))
265 ret = RS2::Millimeter;
267 else if (u==QObject::tr("Centimeter"))
269 ret = RS2::Centimeter;
271 else if (u==QObject::tr("Meter"))
275 else if (u==QObject::tr("Kilometer"))
277 ret = RS2::Kilometer;
279 else if (u==QObject::tr("Microinch"))
281 ret = RS2::Microinch;
283 else if (u==QObject::tr("Mil"))
287 else if (u==QObject::tr("Yard"))
291 else if (u==QObject::tr("Angstrom"))
295 else if (u==QObject::tr("Nanometer"))
297 ret = RS2::Nanometer;
299 else if (u==QObject::tr("Micron"))
303 else if (u==QObject::tr("Decimeter"))
305 ret = RS2::Decimeter;
307 else if (u==QObject::tr("Decameter"))
309 ret = RS2::Decameter;
311 else if (u==QObject::tr("Hectometer"))
313 ret = RS2::Hectometer;
315 else if (u==QObject::tr("Gigameter"))
317 ret = RS2::Gigameter;
319 else if (u==QObject::tr("Astro"))
323 else if (u==QObject::tr("Lightyear"))
325 ret = RS2::Lightyear;
327 else if (u==QObject::tr("Parsec"))
336 * @return true: the unit is metric, false: the unit is imperial.
338 bool RS_Units::isMetric(RS2::Unit u)
340 if (u == RS2::Millimeter || u == RS2::Centimeter || u == RS2::Meter
341 || u == RS2::Kilometer || u == RS2::Angstrom || u == RS2::Nanometer
342 || u == RS2::Micron || u == RS2::Decimeter || u == RS2::Decameter
343 || u == RS2::Hectometer || u == RS2::Gigameter || u == RS2::Astro
344 || u == RS2::Lightyear || u == RS2::Parsec)
351 * @return factor to convert the given unit to Millimeters.
353 double RS_Units::getFactorToMM(RS2::Unit u)
371 case RS2::Millimeter:
374 case RS2::Centimeter:
407 case RS2::Hectometer:
414 ret = 149600000000000.0;
417 ret = 9460731798000000000.0;
420 ret = 30857000000000000000.0;
431 * Converts the given value 'val' from unit 'src' to unit 'dest'.
433 double RS_Units::convert(double val, RS2::Unit src, RS2::Unit dest)
435 if (getFactorToMM(dest) > 0.0)
437 return (val * getFactorToMM(src)) / getFactorToMM(dest);
441 RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Units::convert: invalid factor");
447 * Converts the given vector 'val' from unit 'src' to unit 'dest'.
449 Vector RS_Units::convert(const Vector val, RS2::Unit src, RS2::Unit dest)
451 return Vector(convert(val.x, src, dest), convert(val.y, src, dest), convert(val.z, src, dest));
455 * Formats the given length in the given format.
457 * @param length The length in the current unit of the drawing.
458 * @param format Format of the string.
459 * @param prec Precisision of the value (e.g. 0.001 or 1/128 = 0.0078125)
460 & @param showUnit Append unit to the value.
462 QString RS_Units::formatLinear(double length, RS2::Unit unit, RS2::LinearFormat format,
463 int prec, bool showUnit)
467 // unit appended to value (e.g. 'mm'):
468 /*QString unitString = "";
470 unitString = unitToSign(unit);
473 // barbarian display: show as fraction:
476 case RS2::Scientific:
477 ret = formatScientific(length, unit, prec, showUnit);
481 ret = formatDecimal(length, unit, prec, showUnit);
484 case RS2::Engineering:
485 ret = formatEngineering(length, unit, prec, showUnit);
488 case RS2::Architectural:
489 ret = formatArchitectural(length, unit, prec, showUnit);
492 case RS2::Fractional:
493 ret = formatFractional(length, unit, prec, showUnit);
497 RS_DEBUG->print(RS_Debug::D_WARNING,
498 "RS_Units::formatLinear: Unknown format");
507 * Formats the given length in scientific format (e.g. 2.5E7).
509 * @param length The length in the current unit of the drawing.
510 * @param prec Precisision of the value (e.g. 0.001 or 1/128 = 0.0078125)
511 & @param showUnit Append unit to the value.
513 QString RS_Units::formatScientific(double length, RS2::Unit unit, int prec, bool showUnit)
517 // unit appended to value (e.g. 'mm'):
518 QString unitString = "";
521 unitString = unitToSign(unit);
524 sprintf(format, "%%.%dE%%s", prec);
525 ret.sprintf(format, length, (const char *)unitString.toLocal8Bit());
531 * Formats the given length in decimal (normal) format (e.g. 2.5).
533 * @param length The length in the current unit of the drawing.
534 * @param prec Precisision of the value (e.g. 0.001)
535 & @param showUnit Append unit to the value.
537 QString RS_Units::formatDecimal(double length, RS2::Unit unit, int prec, bool showUnit)
541 // unit appended to value (e.g. 'mm'):
542 QString unitString = "";
545 unitString = unitToSign(unit);
547 ret = RS_Math::doubleToString(length, prec);
556 * Formats the given length in engineering format (e.g. 5' 4.5").
558 * @param length The length in the current unit of the drawing.
559 * @param prec Precisision of the value (e.g. 0.001 or 1/128 = 0.0078125)
560 & @param showUnit Append unit to the value.
562 QString RS_Units::formatEngineering(double length, RS2::Unit /*unit*/, int prec, bool /*showUnit*/)
566 bool sign = (length < 0.0);
567 int feet = (int)floor(fabs(length) / 12);
568 double inches = fabs(length) - feet * 12;
570 QString sInches = RS_Math::doubleToString(inches, prec);
580 ret.sprintf("%d'-%s\"", feet, (const char *)sInches.toLocal8Bit());
584 ret.sprintf("%s\"", (const char *)sInches.toLocal8Bit());
594 * Formats the given length in architectural format (e.g. 5' 4 1/2").
596 * @param length The length in the current unit of the drawing.
597 * @param prec Precisision of the value (e.g. 0.001 or 1/128 = 0.0078125)
598 & @param showUnit Append unit to the value.
600 QString RS_Units::formatArchitectural(double length, RS2::Unit /*unit*/, int prec, bool showUnit)
603 bool neg = (length < 0.0);
604 int feet = (int)floor(fabs(length) / 12);
605 double inches = fabs(length) - feet * 12;
607 QString sInches = formatFractional(inches, RS2::Inch, prec, showUnit);
617 ret.sprintf("-%d'-%s\"", feet, (const char *)sInches.toLocal8Bit());
621 ret.sprintf("%d'-%s\"", feet, (const char *)sInches.toLocal8Bit());
628 * Formats the given length in fractional (barbarian) format (e.g. 5' 3 1/64").
630 * @param length The length in the current unit of the drawing.
631 * @param unit Should be inches.
632 * @param prec Precisision of the value (e.g. 0.001 or 1/128 = 0.0078125)
633 & @param showUnit Append unit to the value.
635 QString RS_Units::formatFractional(double length, RS2::Unit /*unit*/, int prec, bool /*showUnit*/)
639 int num; // number of complete inches (num' 7/128")
640 int nominator; // number of fractions (nominator/128)
641 int denominator; // (4/denominator)
649 length = fabs(length);
652 num = (int)floor(length);
654 denominator = (int)RS_Math::pow(2, prec);
655 nominator = RS_Math::round((length - num) * denominator);
657 // fraction rounds up to 1:
658 if (nominator == denominator)
665 // Simplify the fraction
666 if (nominator != 0 && denominator != 0)
668 int gcd = RS_Math::findGCD(nominator, denominator);
672 nominator = nominator / gcd;
673 denominator = denominator / gcd;
677 RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Units::formatFractional: invalid gcd");
683 if (num != 0 && nominator != 0)
685 ret.sprintf("%s%d %d/%d", (const char *)neg.toLocal8Bit(), num, nominator, denominator);
687 else if (nominator != 0)
689 ret.sprintf("%s%d/%d", (const char *)neg.toLocal8Bit(), nominator, denominator);
693 ret.sprintf("%s%d", (const char *)neg.toLocal8Bit(), num);
704 * Formats the given angle with the given format.
706 * @param angle The angle (always in rad).
707 * @param format Format of the string.
708 * @param prec Precisision of the value (e.g. 0.001 or 1/128 = 0.0078125)
710 * @ret String with the formatted angle.
712 QString RS_Units::formatAngle(double angle, RS2::AngleFormat format, int prec)
719 case RS2::DegreesDecimal:
720 case RS2::DegreesMinutesSeconds:
721 value = RS_Math::rad2deg(angle);
727 value = RS_Math::rad2gra(angle);
730 RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Units::formatAngle: Unknown Angle Unit");
737 case RS2::DegreesDecimal:
740 ret = RS_Math::doubleToString(value, prec);
742 if (format == RS2::DegreesDecimal)
744 if (format == RS2::Radians)
746 if (format == RS2::Gradians)
750 case RS2::DegreesMinutesSeconds:
752 int vDegrees, vMinutes;
754 QString degrees, minutes, seconds;
756 vDegrees = (int)floor(value);
757 vMinutes = (int)floor((value - vDegrees) * 60.0);
758 vSeconds = (value - vDegrees - (vMinutes / 60.0)) * 3600.0;
760 seconds = RS_Math::doubleToString(vSeconds, (prec > 1 ? prec - 2 : 0));
774 if (prec == 0 && vMinutes >= 30.0)
778 else if (prec == 1 && vSeconds >= 30.0)
783 degrees.setNum(vDegrees);
784 minutes.setNum(vMinutes);
789 ret = degrees + QChar(0xB0);
792 ret = degrees + QChar(0xB0) + " " + minutes + "'";
795 ret = degrees + QChar(0xB0) + " " + minutes + "' " + seconds + "\"";
809 * @return Size of the given paper format.
811 Vector RS_Units::paperFormatToSize(RS2::PaperFormat p)
818 ret = Vector(0.0, 0.0);
821 ret = Vector(215.9, 279.4);
824 ret = Vector(215.9, 355.6);
827 ret = Vector(190.5, 254.0);
830 ret = Vector(841.0, 1189.0);
833 ret = Vector(594.0, 841.0);
836 ret = Vector(420.0, 594.0);
839 ret = Vector(297.0, 420.0);
842 ret = Vector(210.0, 297.0);
845 ret = Vector(148.0, 210.0);
848 ret = Vector(105.0, 148.0);
851 ret = Vector(74.0, 105.0);
854 ret = Vector(52.0, 74.0);
857 ret = Vector(37.0, 52.0);
860 ret = Vector(26.0, 37.0);
863 ret = Vector(1000.0, 1414.0);
866 ret = Vector(707.0, 1000.0);
869 ret = Vector(500.0, 707.0);
872 ret = Vector(353.0, 500.0);
875 ret = Vector(250.0, 353.0);
878 ret = Vector(176.0, 250.0);
881 ret = Vector(125.0, 176.0);
884 ret = Vector(88.0, 125.0);
887 ret = Vector(62.0, 88.0);
890 ret = Vector(44.0, 62.0);
893 ret = Vector(31.0, 44.0);
897 ret = Vector(917.0, 1297.0);
900 ret = Vector(648.0, 917.0);
903 ret = Vector(458.0, 648.0);
906 ret = Vector(324.0, 458.0);
909 ret = Vector(229.0, 324.0);
912 ret = Vector(162.0, 229.0);
915 ret = Vector(114.0, 162.0);
918 ret = Vector(81.0, 114.0);
921 ret = Vector(57.0, 81.0);
924 ret = Vector(40.0, 57.0);
927 ret = Vector(28.0, 40.0);
931 ret = Vector(163.0, 229.0);
934 ret = Vector(105.0, 241.0);
937 ret = Vector(110.0, 220.0);
940 ret = Vector(210.0, 330.0);
943 // ret = Vector(432.0, 279.0);
946 ret = Vector(279.0, 432.0);
949 ret = Vector(0.0, 0.0);
959 * Gets the paper format which matches the given size. If no
960 * format matches, RS2::Custom is returned.
962 RS2::PaperFormat RS_Units::paperSizeToFormat(const Vector s)
967 for(int i=(int)RS2::Custom; i<=(int)RS2::NPageSize; ++i)
969 ts1 = RS_Units::paperFormatToSize((RS2::PaperFormat)i);
970 ts2 = Vector(ts1.y, ts1.x);
972 if (ts1.distanceTo(s) < 1.0e-4 || ts2.distanceTo(s) < 1.0e-4)
973 return (RS2::PaperFormat)i;
980 * Converts a paper format to a string (e.g. for a combobox).
982 QString RS_Units::paperFormatToString(RS2::PaperFormat p)
1116 case RS2::NPageSize:
1127 * Converts a string to a paper format.
1129 RS2::PaperFormat RS_Units::stringToPaperFormat(const QString & p)
1131 QString ls = p.toLower();
1132 RS2::PaperFormat ret = RS2::Custom;
1136 else if (p == "letter")
1138 else if (p == "legal")
1140 else if (p == "executive")
1141 ret = RS2::Executive;
1182 else if (p == "b10")
1184 /*else if (p=="c0") {
1186 } else if (p=="c1") {
1188 } else if (p=="c2") {
1190 } else if (p=="c3") {
1192 } else if (p=="c4") {
1194 } else if (p=="c5") {
1196 } else if (p=="c6") {
1198 } else if (p=="c7") {
1200 } else if (p=="c8") {
1202 } else if (p=="c9") {
1204 } else if (p=="c10") {
1207 else if (p == "c5e")
1209 else if (p == "comm10e")
1211 else if (p == "dle")
1213 else if (p == "folio")
1215 //else if (p=="ledger")
1216 // ret = RS2::Ledger;
1217 else if (p == "tabloid")
1219 else if (p == "npagesize")
1220 ret = RS2::NPageSize;
1226 * Performs some testing for the math class.
1228 void RS_Units::test()
1234 std::cout << "RS_Units::test: formatLinear (decimal):\n";
1236 s = RS_Units::formatLinear(v, RS2::Millimeter, RS2::Decimal,
1238 std::cout << "s: " << s << "\n";
1241 s = RS_Units::formatLinear(v, RS2::Millimeter, RS2::Decimal,
1243 std::cout << "s: " << s << "\n";
1246 s = RS_Units::formatLinear(v, RS2::Millimeter, RS2::Decimal,
1248 std::cout << "s: " << s << "\n";
1251 s = RS_Units::formatLinear(v, RS2::Millimeter, RS2::Decimal,
1253 std::cout << "s: " << s << "\n";
1256 s = RS_Units::formatLinear(v, RS2::Millimeter, RS2::Decimal,
1258 std::cout << "s: " << s << "\n";
1261 s = RS_Units::formatLinear(v, RS2::Millimeter, RS2::Decimal,
1263 std::cout << "s: " << s << "\n";
1267 s = RS_Units::formatLinear(v, RS2::Millimeter, RS2::Decimal,
1269 std::cout << "s: " << s << "\n";
1273 std::cout << "RS_Units::test: formatLinear (fractional):\n";
1275 s = RS_Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1277 std::cout << "s: " << s << "\n";
1278 assert(s=="1 13/64");
1281 s = RS_Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1283 std::cout << "s: " << s << "\n";
1284 assert(s=="1 51/256");
1287 s = RS_Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1289 std::cout << "s: " << s << "\n";
1290 assert(s=="51/256");
1293 s = RS_Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1295 std::cout << "s: " << s << "\n";
1299 s = RS_Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1301 std::cout << "s: " << s << "\n";
1305 s = RS_Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1307 std::cout << "s: " << s << "\n";
1311 s = RS_Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1313 std::cout << "s: " << s << "\n";
1317 s = RS_Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1319 std::cout << "s: " << s << "\n";
1323 s = RS_Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1325 std::cout << "s: " << s << "\n";
1329 for(v=11.9999; v<12.0001; v+=0.0000001)
1331 for(int prec=0; prec<=6; ++prec)
1333 s = RS_Units::formatLinear(v, RS2::Inch, RS2::Architectural, prec, true);
1334 std::cout << "prec: " << prec << " v: " << v << " s: " << s.toAscii().data() << "\n";
1338 /*for (v=0.0; v<10.0; v+=0.001) {
1339 s = RS_Units::formatLinear(v, RS2::Foot, RS2::Fractional,
1341 std::cout << "v: " << v << " s: " << s << "\n";
1345 std::cout << "RS_Units::test: formatLinear (scientific):\n";
1347 s = RS_Units::formatLinear(v, RS2::Millimeter, RS2::Scientific,
1349 std::cout << "s: " << s << "\n";
1350 assert(s=="1.0e-3");
1355 std::cout << "RS_Units::test: formatAngle (deg / decimal):\n";
1357 s = RS_Units::formatAngle(v, RS2::DegreesDecimal, 2);
1358 std::cout << "s: " << s << "\n";
1362 s = RS_Units::formatAngle(v, RS2::DegreesDecimal, 2);
1363 std::cout << "s: " << s << "\n";
1367 s = RS_Units::formatAngle(v, RS2::DegreesDecimal, 2);
1368 std::cout << "s: " << s << "\n";
1371 std::cout << "RS_Units::test: formatAngle (deg / d/m/s):\n";
1374 s = RS_Units::formatAngle(v, RS2::DegreesMinutesSeconds, 1);
1375 std::cout << "s: " << s << "\n";
1376 assert(s=="1� 29' 42\"");
1379 s = RS_Units::formatAngle(v, RS2::DegreesMinutesSeconds, 1);
1380 std::cout << "s: " << s << "\n";
1381 assert(s=="1� 30' 0\"");