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 // (C) 2010 Underground Software
8 // JLH = James L. Hammons <jlhamm@acm.org>
11 // --- ---------- -----------------------------------------------------------
12 // JLH 05/05/2010 Moved implementation from header to this file. :-)
22 * Converts a DXF integer () to a Unit enum.
24 RS2::Unit RS_Units::dxfint2unit(int dxfint)
26 return (RS2::Unit)dxfint;
39 return RS2::Millimeter;
41 return RS2::Centimeter;
45 return RS2::Kilometer;
47 return RS2::Microinch;
55 return RS2::Nanometer;
59 return RS2::Decimeter;
61 return RS2::Decameter;
63 return RS2::Hectometer;
65 return RS2::Gigameter;
69 return RS2::Lightyear;
76 * @return a short string representing the given unit (e.g. "mm")
78 QString RS_Units::unitToSign(RS2::Unit u)
132 case RS2::Hectometer:
157 * @return a string representing the given unit (e.g. "Millimeter").
158 * translated if @a t is true (the default).
160 QString RS_Units::unitToString(RS2::Unit u, bool t)
167 ret = t ? QObject::tr("None") : QString("None");
170 ret = t ? QObject::tr("Inch") : QString("Inch");
173 ret = t ? QObject::tr("Foot") : QString("Foot");
176 ret = t ? QObject::tr("Mile") : QString("Mile");
178 case RS2::Millimeter:
179 ret = t ? QObject::tr("Millimeter") : QString("Millimeter");
181 case RS2::Centimeter:
182 ret = t ? QObject::tr("Centimeter") : QString("Centimeter");
185 ret = t ? QObject::tr("Meter") : QString("Meter");
188 ret = t ? QObject::tr("Kilometer") : QString("Kilometer");
191 ret = t ? QObject::tr("Microinch") : QString("Microinch");
194 ret = t ? QObject::tr("Mil") : QString("Mil");
197 ret = t ? QObject::tr("Yard") : QString("Yard");
200 ret = t ? QObject::tr("Angstrom") : QString("Angstrom");
203 ret = t ? QObject::tr("Nanometer") : QString("Nanometer");
206 ret = t ? QObject::tr("Micron") : QString("Micron");
209 ret = t ? QObject::tr("Decimeter") : QString("Decimeter");
212 ret = t ? QObject::tr("Decameter") : QString("Decameter");
214 case RS2::Hectometer:
215 ret = t ? QObject::tr("Hectometer") : QString("Hectometer");
218 ret = t ? QObject::tr("Gigameter") : QString("Gigameter");
221 ret = t ? QObject::tr("Astro") : QString("Astro");
224 ret = t ? QObject::tr("Lightyear") : QString("Lightyear");
227 ret = t ? QObject::tr("Parsec") : QString("Parsec");
239 * Converts a string into a unit enum.
241 RS2::Unit RS_Units::stringToUnit(const QString & u)
243 RS2::Unit ret = RS2::None;
249 else if (u == QObject::tr("Inch"))
253 else if (u==QObject::tr("Foot"))
257 else if (u==QObject::tr("Mile"))
261 else if (u==QObject::tr("Millimeter"))
263 ret = RS2::Millimeter;
265 else if (u==QObject::tr("Centimeter"))
267 ret = RS2::Centimeter;
269 else if (u==QObject::tr("Meter"))
273 else if (u==QObject::tr("Kilometer"))
275 ret = RS2::Kilometer;
277 else if (u==QObject::tr("Microinch"))
279 ret = RS2::Microinch;
281 else if (u==QObject::tr("Mil"))
285 else if (u==QObject::tr("Yard"))
289 else if (u==QObject::tr("Angstrom"))
293 else if (u==QObject::tr("Nanometer"))
295 ret = RS2::Nanometer;
297 else if (u==QObject::tr("Micron"))
301 else if (u==QObject::tr("Decimeter"))
303 ret = RS2::Decimeter;
305 else if (u==QObject::tr("Decameter"))
307 ret = RS2::Decameter;
309 else if (u==QObject::tr("Hectometer"))
311 ret = RS2::Hectometer;
313 else if (u==QObject::tr("Gigameter"))
315 ret = RS2::Gigameter;
317 else if (u==QObject::tr("Astro"))
321 else if (u==QObject::tr("Lightyear"))
323 ret = RS2::Lightyear;
325 else if (u==QObject::tr("Parsec"))
334 * @return true: the unit is metric, false: the unit is imperial.
336 bool RS_Units::isMetric(RS2::Unit u)
338 if (u == RS2::Millimeter || u == RS2::Centimeter || u == RS2::Meter
339 || u == RS2::Kilometer || u == RS2::Angstrom || u == RS2::Nanometer
340 || u == RS2::Micron || u == RS2::Decimeter || u == RS2::Decameter
341 || u == RS2::Hectometer || u == RS2::Gigameter || u == RS2::Astro
342 || u == RS2::Lightyear || u == RS2::Parsec)
349 * @return factor to convert the given unit to Millimeters.
351 double RS_Units::getFactorToMM(RS2::Unit u)
369 case RS2::Millimeter:
372 case RS2::Centimeter:
405 case RS2::Hectometer:
412 ret = 149600000000000.0;
415 ret = 9460731798000000000.0;
418 ret = 30857000000000000000.0;
429 * Converts the given value 'val' from unit 'src' to unit 'dest'.
431 double RS_Units::convert(double val, RS2::Unit src, RS2::Unit dest)
433 if (getFactorToMM(dest) > 0.0)
435 return (val * getFactorToMM(src)) / getFactorToMM(dest);
439 RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Units::convert: invalid factor");
445 * Converts the given vector 'val' from unit 'src' to unit 'dest'.
447 Vector RS_Units::convert(const Vector val, RS2::Unit src, RS2::Unit dest)
449 return Vector(convert(val.x, src, dest), convert(val.y, src, dest), convert(val.z, src, dest));
453 * Formats the given length in the given format.
455 * @param length The length in the current unit of the drawing.
456 * @param format Format of the string.
457 * @param prec Precisision of the value (e.g. 0.001 or 1/128 = 0.0078125)
458 & @param showUnit Append unit to the value.
460 QString RS_Units::formatLinear(double length, RS2::Unit unit, RS2::LinearFormat format,
461 int prec, bool showUnit)
465 // unit appended to value (e.g. 'mm'):
466 /*QString unitString = "";
468 unitString = unitToSign(unit);
471 // barbarian display: show as fraction:
474 case RS2::Scientific:
475 ret = formatScientific(length, unit, prec, showUnit);
479 ret = formatDecimal(length, unit, prec, showUnit);
482 case RS2::Engineering:
483 ret = formatEngineering(length, unit, prec, showUnit);
486 case RS2::Architectural:
487 ret = formatArchitectural(length, unit, prec, showUnit);
490 case RS2::Fractional:
491 ret = formatFractional(length, unit, prec, showUnit);
495 RS_DEBUG->print(RS_Debug::D_WARNING,
496 "RS_Units::formatLinear: Unknown format");
505 * Formats the given length in scientific format (e.g. 2.5E7).
507 * @param length The length in the current unit of the drawing.
508 * @param prec Precisision of the value (e.g. 0.001 or 1/128 = 0.0078125)
509 & @param showUnit Append unit to the value.
511 QString RS_Units::formatScientific(double length, RS2::Unit unit, int prec, bool showUnit)
515 // unit appended to value (e.g. 'mm'):
516 QString unitString = "";
519 unitString = unitToSign(unit);
522 sprintf(format, "%%.%dE%%s", prec);
523 ret.sprintf(format, length, (const char *)unitString.toLocal8Bit());
529 * Formats the given length in decimal (normal) format (e.g. 2.5).
531 * @param length The length in the current unit of the drawing.
532 * @param prec Precisision of the value (e.g. 0.001)
533 & @param showUnit Append unit to the value.
535 QString RS_Units::formatDecimal(double length, RS2::Unit unit, int prec, bool showUnit)
539 // unit appended to value (e.g. 'mm'):
540 QString unitString = "";
543 unitString = unitToSign(unit);
545 ret = RS_Math::doubleToString(length, prec);
554 * Formats the given length in engineering format (e.g. 5' 4.5").
556 * @param length The length in the current unit of the drawing.
557 * @param prec Precisision of the value (e.g. 0.001 or 1/128 = 0.0078125)
558 & @param showUnit Append unit to the value.
560 QString RS_Units::formatEngineering(double length, RS2::Unit /*unit*/, int prec, bool /*showUnit*/)
564 bool sign = (length < 0.0);
565 int feet = (int)floor(fabs(length) / 12);
566 double inches = fabs(length) - feet * 12;
568 QString sInches = RS_Math::doubleToString(inches, prec);
578 ret.sprintf("%d'-%s\"", feet, (const char *)sInches.toLocal8Bit());
582 ret.sprintf("%s\"", (const char *)sInches.toLocal8Bit());
592 * Formats the given length in architectural format (e.g. 5' 4 1/2").
594 * @param length The length in the current unit of the drawing.
595 * @param prec Precisision of the value (e.g. 0.001 or 1/128 = 0.0078125)
596 & @param showUnit Append unit to the value.
598 QString RS_Units::formatArchitectural(double length, RS2::Unit /*unit*/, int prec, bool showUnit)
601 bool neg = (length < 0.0);
602 int feet = (int)floor(fabs(length) / 12);
603 double inches = fabs(length) - feet * 12;
605 QString sInches = formatFractional(inches, RS2::Inch, prec, showUnit);
615 ret.sprintf("-%d'-%s\"", feet, (const char *)sInches.toLocal8Bit());
619 ret.sprintf("%d'-%s\"", feet, (const char *)sInches.toLocal8Bit());
626 * Formats the given length in fractional (barbarian) format (e.g. 5' 3 1/64").
628 * @param length The length in the current unit of the drawing.
629 * @param unit Should be inches.
630 * @param prec Precisision of the value (e.g. 0.001 or 1/128 = 0.0078125)
631 & @param showUnit Append unit to the value.
633 QString RS_Units::formatFractional(double length, RS2::Unit /*unit*/, int prec, bool /*showUnit*/)
637 int num; // number of complete inches (num' 7/128")
638 int nominator; // number of fractions (nominator/128)
639 int denominator; // (4/denominator)
647 length = fabs(length);
650 num = (int)floor(length);
652 denominator = (int)RS_Math::pow(2, prec);
653 nominator = RS_Math::round((length - num) * denominator);
655 // fraction rounds up to 1:
656 if (nominator == denominator)
663 // Simplify the fraction
664 if (nominator != 0 && denominator != 0)
666 int gcd = RS_Math::findGCD(nominator, denominator);
670 nominator = nominator / gcd;
671 denominator = denominator / gcd;
675 RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Units::formatFractional: invalid gcd");
681 if (num != 0 && nominator != 0)
683 ret.sprintf("%s%d %d/%d", (const char *)neg.toLocal8Bit(), num, nominator, denominator);
685 else if (nominator != 0)
687 ret.sprintf("%s%d/%d", (const char *)neg.toLocal8Bit(), nominator, denominator);
691 ret.sprintf("%s%d", (const char *)neg.toLocal8Bit(), num);
702 * Formats the given angle with the given format.
704 * @param angle The angle (always in rad).
705 * @param format Format of the string.
706 * @param prec Precisision of the value (e.g. 0.001 or 1/128 = 0.0078125)
708 * @ret String with the formatted angle.
710 QString RS_Units::formatAngle(double angle, RS2::AngleFormat format, int prec)
717 case RS2::DegreesDecimal:
718 case RS2::DegreesMinutesSeconds:
719 value = RS_Math::rad2deg(angle);
725 value = RS_Math::rad2gra(angle);
728 RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Units::formatAngle: Unknown Angle Unit");
735 case RS2::DegreesDecimal:
738 ret = RS_Math::doubleToString(value, prec);
740 if (format == RS2::DegreesDecimal)
742 if (format == RS2::Radians)
744 if (format == RS2::Gradians)
748 case RS2::DegreesMinutesSeconds:
750 int vDegrees, vMinutes;
752 QString degrees, minutes, seconds;
754 vDegrees = (int)floor(value);
755 vMinutes = (int)floor((value - vDegrees) * 60.0);
756 vSeconds = (value - vDegrees - (vMinutes / 60.0)) * 3600.0;
758 seconds = RS_Math::doubleToString(vSeconds, (prec > 1 ? prec - 2 : 0));
772 if (prec == 0 && vMinutes >= 30.0)
776 else if (prec == 1 && vSeconds >= 30.0)
781 degrees.setNum(vDegrees);
782 minutes.setNum(vMinutes);
787 ret = degrees + QChar(0xB0);
790 ret = degrees + QChar(0xB0) + " " + minutes + "'";
793 ret = degrees + QChar(0xB0) + " " + minutes + "' " + seconds + "\"";
807 * @return Size of the given paper format.
809 Vector RS_Units::paperFormatToSize(RS2::PaperFormat p)
816 ret = Vector(0.0, 0.0);
819 ret = Vector(215.9, 279.4);
822 ret = Vector(215.9, 355.6);
825 ret = Vector(190.5, 254.0);
828 ret = Vector(841.0, 1189.0);
831 ret = Vector(594.0, 841.0);
834 ret = Vector(420.0, 594.0);
837 ret = Vector(297.0, 420.0);
840 ret = Vector(210.0, 297.0);
843 ret = Vector(148.0, 210.0);
846 ret = Vector(105.0, 148.0);
849 ret = Vector(74.0, 105.0);
852 ret = Vector(52.0, 74.0);
855 ret = Vector(37.0, 52.0);
858 ret = Vector(26.0, 37.0);
861 ret = Vector(1000.0, 1414.0);
864 ret = Vector(707.0, 1000.0);
867 ret = Vector(500.0, 707.0);
870 ret = Vector(353.0, 500.0);
873 ret = Vector(250.0, 353.0);
876 ret = Vector(176.0, 250.0);
879 ret = Vector(125.0, 176.0);
882 ret = Vector(88.0, 125.0);
885 ret = Vector(62.0, 88.0);
888 ret = Vector(44.0, 62.0);
891 ret = Vector(31.0, 44.0);
895 ret = Vector(917.0, 1297.0);
898 ret = Vector(648.0, 917.0);
901 ret = Vector(458.0, 648.0);
904 ret = Vector(324.0, 458.0);
907 ret = Vector(229.0, 324.0);
910 ret = Vector(162.0, 229.0);
913 ret = Vector(114.0, 162.0);
916 ret = Vector(81.0, 114.0);
919 ret = Vector(57.0, 81.0);
922 ret = Vector(40.0, 57.0);
925 ret = Vector(28.0, 40.0);
929 ret = Vector(163.0, 229.0);
932 ret = Vector(105.0, 241.0);
935 ret = Vector(110.0, 220.0);
938 ret = Vector(210.0, 330.0);
941 // ret = Vector(432.0, 279.0);
944 ret = Vector(279.0, 432.0);
947 ret = Vector(0.0, 0.0);
957 * Gets the paper format which matches the given size. If no
958 * format matches, RS2::Custom is returned.
960 RS2::PaperFormat RS_Units::paperSizeToFormat(const Vector s)
965 for(int i=(int)RS2::Custom; i<=(int)RS2::NPageSize; ++i)
967 ts1 = RS_Units::paperFormatToSize((RS2::PaperFormat)i);
968 ts2 = Vector(ts1.y, ts1.x);
970 if (ts1.distanceTo(s) < 1.0e-4 || ts2.distanceTo(s) < 1.0e-4)
971 return (RS2::PaperFormat)i;
978 * Converts a paper format to a string (e.g. for a combobox).
980 QString RS_Units::paperFormatToString(RS2::PaperFormat p)
1114 case RS2::NPageSize:
1125 * Converts a string to a paper format.
1127 RS2::PaperFormat RS_Units::stringToPaperFormat(const QString & p)
1129 QString ls = p.toLower();
1130 RS2::PaperFormat ret = RS2::Custom;
1134 else if (p == "letter")
1136 else if (p == "legal")
1138 else if (p == "executive")
1139 ret = RS2::Executive;
1180 else if (p == "b10")
1182 /*else if (p=="c0") {
1184 } else if (p=="c1") {
1186 } else if (p=="c2") {
1188 } else if (p=="c3") {
1190 } else if (p=="c4") {
1192 } else if (p=="c5") {
1194 } else if (p=="c6") {
1196 } else if (p=="c7") {
1198 } else if (p=="c8") {
1200 } else if (p=="c9") {
1202 } else if (p=="c10") {
1205 else if (p == "c5e")
1207 else if (p == "comm10e")
1209 else if (p == "dle")
1211 else if (p == "folio")
1213 //else if (p=="ledger")
1214 // ret = RS2::Ledger;
1215 else if (p == "tabloid")
1217 else if (p == "npagesize")
1218 ret = RS2::NPageSize;
1224 * Performs some testing for the math class.
1226 void RS_Units::test()
1232 std::cout << "RS_Units::test: formatLinear (decimal):\n";
1234 s = RS_Units::formatLinear(v, RS2::Millimeter, RS2::Decimal,
1236 std::cout << "s: " << s << "\n";
1239 s = RS_Units::formatLinear(v, RS2::Millimeter, RS2::Decimal,
1241 std::cout << "s: " << s << "\n";
1244 s = RS_Units::formatLinear(v, RS2::Millimeter, RS2::Decimal,
1246 std::cout << "s: " << s << "\n";
1249 s = RS_Units::formatLinear(v, RS2::Millimeter, RS2::Decimal,
1251 std::cout << "s: " << s << "\n";
1254 s = RS_Units::formatLinear(v, RS2::Millimeter, RS2::Decimal,
1256 std::cout << "s: " << s << "\n";
1259 s = RS_Units::formatLinear(v, RS2::Millimeter, RS2::Decimal,
1261 std::cout << "s: " << s << "\n";
1265 s = RS_Units::formatLinear(v, RS2::Millimeter, RS2::Decimal,
1267 std::cout << "s: " << s << "\n";
1271 std::cout << "RS_Units::test: formatLinear (fractional):\n";
1273 s = RS_Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1275 std::cout << "s: " << s << "\n";
1276 assert(s=="1 13/64");
1279 s = RS_Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1281 std::cout << "s: " << s << "\n";
1282 assert(s=="1 51/256");
1285 s = RS_Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1287 std::cout << "s: " << s << "\n";
1288 assert(s=="51/256");
1291 s = RS_Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1293 std::cout << "s: " << s << "\n";
1297 s = RS_Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1299 std::cout << "s: " << s << "\n";
1303 s = RS_Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1305 std::cout << "s: " << s << "\n";
1309 s = RS_Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1311 std::cout << "s: " << s << "\n";
1315 s = RS_Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1317 std::cout << "s: " << s << "\n";
1321 s = RS_Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1323 std::cout << "s: " << s << "\n";
1327 for(v=11.9999; v<12.0001; v+=0.0000001)
1329 for(int prec=0; prec<=6; ++prec)
1331 s = RS_Units::formatLinear(v, RS2::Inch, RS2::Architectural, prec, true);
1332 std::cout << "prec: " << prec << " v: " << v << " s: " << s.toAscii().data() << "\n";
1336 /*for (v=0.0; v<10.0; v+=0.001) {
1337 s = RS_Units::formatLinear(v, RS2::Foot, RS2::Fractional,
1339 std::cout << "v: " << v << " s: " << s << "\n";
1343 std::cout << "RS_Units::test: formatLinear (scientific):\n";
1345 s = RS_Units::formatLinear(v, RS2::Millimeter, RS2::Scientific,
1347 std::cout << "s: " << s << "\n";
1348 assert(s=="1.0e-3");
1353 std::cout << "RS_Units::test: formatAngle (deg / decimal):\n";
1355 s = RS_Units::formatAngle(v, RS2::DegreesDecimal, 2);
1356 std::cout << "s: " << s << "\n";
1360 s = RS_Units::formatAngle(v, RS2::DegreesDecimal, 2);
1361 std::cout << "s: " << s << "\n";
1365 s = RS_Units::formatAngle(v, RS2::DegreesDecimal, 2);
1366 std::cout << "s: " << s << "\n";
1369 std::cout << "RS_Units::test: formatAngle (deg / d/m/s):\n";
1372 s = RS_Units::formatAngle(v, RS2::DegreesMinutesSeconds, 1);
1373 std::cout << "s: " << s << "\n";
1374 assert(s=="1� 29' 42\"");
1377 s = RS_Units::formatAngle(v, RS2::DegreesMinutesSeconds, 1);
1378 std::cout << "s: " << s << "\n";
1379 assert(s=="1� 30' 0\"");