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. :-)
20 #include "mathextra.h"
24 * Converts a DXF integer () to a Unit enum.
26 RS2::Unit 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 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 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 Units::stringToUnit(const QString & u)
245 RS2::Unit ret = RS2::None;
249 else if (u == QObject::tr("Inch"))
251 else if (u == QObject::tr("Foot"))
253 else if (u == QObject::tr("Mile"))
255 else if (u == QObject::tr("Millimeter"))
256 ret = RS2::Millimeter;
257 else if (u == QObject::tr("Centimeter"))
258 ret = RS2::Centimeter;
259 else if (u == QObject::tr("Meter"))
261 else if (u == QObject::tr("Kilometer"))
262 ret = RS2::Kilometer;
263 else if (u == QObject::tr("Microinch"))
264 ret = RS2::Microinch;
265 else if (u == QObject::tr("Mil"))
267 else if (u == QObject::tr("Yard"))
269 else if (u == QObject::tr("Angstrom"))
271 else if (u == QObject::tr("Nanometer"))
272 ret = RS2::Nanometer;
273 else if (u == QObject::tr("Micron"))
275 else if (u == QObject::tr("Decimeter"))
276 ret = RS2::Decimeter;
277 else if (u == QObject::tr("Decameter"))
278 ret = RS2::Decameter;
279 else if (u == QObject::tr("Hectometer"))
280 ret = RS2::Hectometer;
281 else if (u == QObject::tr("Gigameter"))
282 ret = RS2::Gigameter;
283 else if (u == QObject::tr("Astro"))
285 else if (u == QObject::tr("Lightyear"))
286 ret = RS2::Lightyear;
287 else if (u == QObject::tr("Parsec"))
294 * @return true: the unit is metric, false: the unit is imperial.
296 bool Units::isMetric(RS2::Unit u)
298 if (u == RS2::Millimeter || u == RS2::Centimeter || u == RS2::Meter
299 || u == RS2::Kilometer || u == RS2::Angstrom || u == RS2::Nanometer
300 || u == RS2::Micron || u == RS2::Decimeter || u == RS2::Decameter
301 || u == RS2::Hectometer || u == RS2::Gigameter || u == RS2::Astro
302 || u == RS2::Lightyear || u == RS2::Parsec)
309 * @return factor to convert the given unit to Millimeters.
311 double Units::getFactorToMM(RS2::Unit u)
329 case RS2::Millimeter:
332 case RS2::Centimeter:
365 case RS2::Hectometer:
372 ret = 149600000000000.0;
375 ret = 9460731798000000000.0;
378 ret = 30857000000000000000.0;
389 * Converts the given value 'val' from unit 'src' to unit 'dest'.
391 double Units::convert(double val, RS2::Unit src, RS2::Unit dest)
393 if (getFactorToMM(dest) > 0.0)
395 return (val * getFactorToMM(src)) / getFactorToMM(dest);
399 DEBUG->print(Debug::D_WARNING, "Units::convert: invalid factor");
405 * Converts the given vector 'val' from unit 'src' to unit 'dest'.
407 Vector Units::convert(const Vector val, RS2::Unit src, RS2::Unit dest)
409 return Vector(convert(val.x, src, dest), convert(val.y, src, dest), convert(val.z, src, dest));
413 * Formats the given length in the given format.
415 * @param length The length in the current unit of the drawing.
416 * @param format Format of the string.
417 * @param prec Precisision of the value (e.g. 0.001 or 1/128 = 0.0078125)
418 & @param showUnit Append unit to the value.
420 QString Units::formatLinear(double length, RS2::Unit unit, RS2::LinearFormat format,
421 int prec, bool showUnit)
425 // unit appended to value (e.g. 'mm'):
426 /*QString unitString = "";
428 unitString = unitToSign(unit);
431 // Barbarian display: Show as fraction:
434 case RS2::Scientific:
435 ret = formatScientific(length, unit, prec, showUnit);
439 ret = formatDecimal(length, unit, prec, showUnit);
442 case RS2::Engineering:
443 ret = formatEngineering(length, unit, prec, showUnit);
446 case RS2::Architectural:
447 ret = formatArchitectural(length, unit, prec, showUnit);
450 case RS2::Fractional:
451 ret = formatFractional(length, unit, prec, showUnit);
455 DEBUG->print(Debug::D_WARNING,
456 "Units::formatLinear: Unknown format");
465 * Formats the given length in scientific format (e.g. 2.5E7).
467 * @param length The length in the current unit of the drawing.
468 * @param prec Precisision of the value (e.g. 0.001 or 1/128 = 0.0078125)
469 & @param showUnit Append unit to the value.
471 QString Units::formatScientific(double length, RS2::Unit unit, int prec, bool showUnit)
475 // unit appended to value (e.g. 'mm'):
476 QString unitString = "";
479 unitString = unitToSign(unit);
482 sprintf(format, "%%.%dE%%s", prec);
483 ret.sprintf(format, length, (const char *)unitString.toLocal8Bit());
489 * Formats the given length in decimal (normal) format (e.g. 2.5).
491 * @param length The length in the current unit of the drawing.
492 * @param prec Precisision of the value (e.g. 0.001)
493 & @param showUnit Append unit to the value.
495 QString Units::formatDecimal(double length, RS2::Unit unit, int prec, bool showUnit)
499 // unit appended to value (e.g. 'mm'):
500 QString unitString = "";
503 unitString = unitToSign(unit);
505 ret = Math::doubleToString(length, prec);
514 * Formats the given length in engineering format (e.g. 5' 4.5").
516 * @param length The length in the current unit of the drawing.
517 * @param prec Precisision of the value (e.g. 0.001 or 1/128 = 0.0078125)
518 & @param showUnit Append unit to the value.
520 QString Units::formatEngineering(double length, RS2::Unit /*unit*/, int prec, bool /*showUnit*/)
524 bool sign = (length < 0.0);
525 int feet = (int)floor(fabs(length) / 12);
526 double inches = fabs(length) - feet * 12;
528 QString sInches = Math::doubleToString(inches, prec);
538 ret.sprintf("%d'-%s\"", feet, (const char *)sInches.toLocal8Bit());
542 ret.sprintf("%s\"", (const char *)sInches.toLocal8Bit());
552 * Formats the given length in architectural format (e.g. 5' 4 1/2").
554 * @param length The length in the current unit of the drawing.
555 * @param prec Precisision of the value (e.g. 0.001 or 1/128 = 0.0078125)
556 & @param showUnit Append unit to the value.
558 QString Units::formatArchitectural(double length, RS2::Unit /*unit*/, int prec, bool showUnit)
561 bool neg = (length < 0.0);
562 int feet = (int)floor(fabs(length) / 12);
563 double inches = fabs(length) - feet * 12;
565 QString sInches = formatFractional(inches, RS2::Inch, prec, showUnit);
575 ret.sprintf("-%d'-%s\"", feet, (const char *)sInches.toLocal8Bit());
579 ret.sprintf("%d'-%s\"", feet, (const char *)sInches.toLocal8Bit());
586 * Formats the given length in fractional (barbarian) format (e.g. 5' 3 1/64").
588 * @param length The length in the current unit of the drawing.
589 * @param unit Should be inches.
590 * @param prec Precisision of the value (e.g. 0.001 or 1/128 = 0.0078125)
591 & @param showUnit Append unit to the value.
593 QString Units::formatFractional(double length, RS2::Unit /*unit*/, int prec, bool /*showUnit*/)
597 int num; // number of complete inches (num' 7/128")
598 int nominator; // number of fractions (nominator/128)
599 int denominator; // (4/denominator)
607 length = fabs(length);
610 num = (int)floor(length);
612 denominator = (int)Math::pow(2, prec);
613 nominator = Math::round((length - num) * denominator);
615 // fraction rounds up to 1:
616 if (nominator == denominator)
623 // Simplify the fraction
624 if (nominator != 0 && denominator != 0)
626 int gcd = Math::findGCD(nominator, denominator);
630 nominator = nominator / gcd;
631 denominator = denominator / gcd;
635 DEBUG->print(Debug::D_WARNING, "Units::formatFractional: invalid gcd");
641 if (num != 0 && nominator != 0)
643 ret.sprintf("%s%d %d/%d", (const char *)neg.toLocal8Bit(), num, nominator, denominator);
645 else if (nominator != 0)
647 ret.sprintf("%s%d/%d", (const char *)neg.toLocal8Bit(), nominator, denominator);
651 ret.sprintf("%s%d", (const char *)neg.toLocal8Bit(), num);
662 * Formats the given angle with the given format.
664 * @param angle The angle (always in rad).
665 * @param format Format of the string.
666 * @param prec Precisision of the value (e.g. 0.001 or 1/128 = 0.0078125)
668 * @ret String with the formatted angle.
670 QString Units::formatAngle(double angle, RS2::AngleFormat format, int prec)
677 case RS2::DegreesDecimal:
678 case RS2::DegreesMinutesSeconds:
679 value = Math::rad2deg(angle);
685 value = Math::rad2gra(angle);
688 DEBUG->print(Debug::D_WARNING, "Units::formatAngle: Unknown Angle Unit");
695 case RS2::DegreesDecimal:
698 ret = Math::doubleToString(value, prec);
700 if (format == RS2::DegreesDecimal)
702 if (format == RS2::Radians)
704 if (format == RS2::Gradians)
708 case RS2::DegreesMinutesSeconds:
710 int vDegrees, vMinutes;
712 QString degrees, minutes, seconds;
714 vDegrees = (int)floor(value);
715 vMinutes = (int)floor((value - vDegrees) * 60.0);
716 vSeconds = (value - vDegrees - (vMinutes / 60.0)) * 3600.0;
718 seconds = Math::doubleToString(vSeconds, (prec > 1 ? prec - 2 : 0));
732 if (prec == 0 && vMinutes >= 30.0)
736 else if (prec == 1 && vSeconds >= 30.0)
741 degrees.setNum(vDegrees);
742 minutes.setNum(vMinutes);
747 ret = degrees + QChar(0xB0);
750 ret = degrees + QChar(0xB0) + " " + minutes + "'";
753 ret = degrees + QChar(0xB0) + " " + minutes + "' " + seconds + "\"";
767 * @return Size of the given paper format.
769 Vector Units::paperFormatToSize(RS2::PaperFormat p)
776 ret = Vector(0.0, 0.0);
779 ret = Vector(215.9, 279.4);
782 ret = Vector(215.9, 355.6);
785 ret = Vector(190.5, 254.0);
788 ret = Vector(841.0, 1189.0);
791 ret = Vector(594.0, 841.0);
794 ret = Vector(420.0, 594.0);
797 ret = Vector(297.0, 420.0);
800 ret = Vector(210.0, 297.0);
803 ret = Vector(148.0, 210.0);
806 ret = Vector(105.0, 148.0);
809 ret = Vector(74.0, 105.0);
812 ret = Vector(52.0, 74.0);
815 ret = Vector(37.0, 52.0);
818 ret = Vector(26.0, 37.0);
821 ret = Vector(1000.0, 1414.0);
824 ret = Vector(707.0, 1000.0);
827 ret = Vector(500.0, 707.0);
830 ret = Vector(353.0, 500.0);
833 ret = Vector(250.0, 353.0);
836 ret = Vector(176.0, 250.0);
839 ret = Vector(125.0, 176.0);
842 ret = Vector(88.0, 125.0);
845 ret = Vector(62.0, 88.0);
848 ret = Vector(44.0, 62.0);
851 ret = Vector(31.0, 44.0);
855 ret = Vector(917.0, 1297.0);
858 ret = Vector(648.0, 917.0);
861 ret = Vector(458.0, 648.0);
864 ret = Vector(324.0, 458.0);
867 ret = Vector(229.0, 324.0);
870 ret = Vector(162.0, 229.0);
873 ret = Vector(114.0, 162.0);
876 ret = Vector(81.0, 114.0);
879 ret = Vector(57.0, 81.0);
882 ret = Vector(40.0, 57.0);
885 ret = Vector(28.0, 40.0);
889 ret = Vector(163.0, 229.0);
892 ret = Vector(105.0, 241.0);
895 ret = Vector(110.0, 220.0);
898 ret = Vector(210.0, 330.0);
901 // ret = Vector(432.0, 279.0);
904 ret = Vector(279.0, 432.0);
907 ret = Vector(0.0, 0.0);
917 * Gets the paper format which matches the given size. If no
918 * format matches, RS2::Custom is returned.
920 RS2::PaperFormat Units::paperSizeToFormat(const Vector s)
925 for(int i=(int)RS2::Custom; i<=(int)RS2::NPageSize; ++i)
927 ts1 = Units::paperFormatToSize((RS2::PaperFormat)i);
928 ts2 = Vector(ts1.y, ts1.x);
930 if (ts1.distanceTo(s) < 1.0e-4 || ts2.distanceTo(s) < 1.0e-4)
931 return (RS2::PaperFormat)i;
938 * Converts a paper format to a string (e.g. for a combobox).
940 QString Units::paperFormatToString(RS2::PaperFormat p)
1074 case RS2::NPageSize:
1085 * Converts a string to a paper format.
1087 RS2::PaperFormat Units::stringToPaperFormat(const QString & p)
1089 QString ls = p.toLower();
1090 RS2::PaperFormat ret = RS2::Custom;
1094 else if (p == "letter")
1096 else if (p == "legal")
1098 else if (p == "executive")
1099 ret = RS2::Executive;
1140 else if (p == "b10")
1142 /*else if (p=="c0") {
1144 } else if (p=="c1") {
1146 } else if (p=="c2") {
1148 } else if (p=="c3") {
1150 } else if (p=="c4") {
1152 } else if (p=="c5") {
1154 } else if (p=="c6") {
1156 } else if (p=="c7") {
1158 } else if (p=="c8") {
1160 } else if (p=="c9") {
1162 } else if (p=="c10") {
1165 else if (p == "c5e")
1167 else if (p == "comm10e")
1169 else if (p == "dle")
1171 else if (p == "folio")
1173 //else if (p=="ledger")
1174 // ret = RS2::Ledger;
1175 else if (p == "tabloid")
1177 else if (p == "npagesize")
1178 ret = RS2::NPageSize;
1184 * Performs some testing for the math class.
1192 std::cout << "Units::test: formatLinear (decimal):\n";
1194 s = Units::formatLinear(v, RS2::Millimeter, RS2::Decimal,
1196 std::cout << "s: " << s << "\n";
1199 s = Units::formatLinear(v, RS2::Millimeter, RS2::Decimal,
1201 std::cout << "s: " << s << "\n";
1204 s = Units::formatLinear(v, RS2::Millimeter, RS2::Decimal,
1206 std::cout << "s: " << s << "\n";
1209 s = Units::formatLinear(v, RS2::Millimeter, RS2::Decimal,
1211 std::cout << "s: " << s << "\n";
1214 s = Units::formatLinear(v, RS2::Millimeter, RS2::Decimal,
1216 std::cout << "s: " << s << "\n";
1219 s = Units::formatLinear(v, RS2::Millimeter, RS2::Decimal,
1221 std::cout << "s: " << s << "\n";
1225 s = Units::formatLinear(v, RS2::Millimeter, RS2::Decimal,
1227 std::cout << "s: " << s << "\n";
1231 std::cout << "Units::test: formatLinear (fractional):\n";
1233 s = Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1235 std::cout << "s: " << s << "\n";
1236 assert(s=="1 13/64");
1239 s = Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1241 std::cout << "s: " << s << "\n";
1242 assert(s=="1 51/256");
1245 s = Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1247 std::cout << "s: " << s << "\n";
1248 assert(s=="51/256");
1251 s = Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1253 std::cout << "s: " << s << "\n";
1257 s = Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1259 std::cout << "s: " << s << "\n";
1263 s = Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1265 std::cout << "s: " << s << "\n";
1269 s = Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1271 std::cout << "s: " << s << "\n";
1275 s = Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1277 std::cout << "s: " << s << "\n";
1281 s = Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1283 std::cout << "s: " << s << "\n";
1287 for(v=11.9999; v<12.0001; v+=0.0000001)
1289 for(int prec=0; prec<=6; ++prec)
1291 s = Units::formatLinear(v, RS2::Inch, RS2::Architectural, prec, true);
1292 std::cout << "prec: " << prec << " v: " << v << " s: " << s.toAscii().data() << "\n";
1296 /*for (v=0.0; v<10.0; v+=0.001) {
1297 s = Units::formatLinear(v, RS2::Foot, RS2::Fractional,
1299 std::cout << "v: " << v << " s: " << s << "\n";
1303 std::cout << "Units::test: formatLinear (scientific):\n";
1305 s = Units::formatLinear(v, RS2::Millimeter, RS2::Scientific,
1307 std::cout << "s: " << s << "\n";
1308 assert(s=="1.0e-3");
1313 std::cout << "Units::test: formatAngle (deg / decimal):\n";
1315 s = Units::formatAngle(v, RS2::DegreesDecimal, 2);
1316 std::cout << "s: " << s << "\n";
1320 s = Units::formatAngle(v, RS2::DegreesDecimal, 2);
1321 std::cout << "s: " << s << "\n";
1325 s = Units::formatAngle(v, RS2::DegreesDecimal, 2);
1326 std::cout << "s: " << s << "\n";
1329 std::cout << "Units::test: formatAngle (deg / d/m/s):\n";
1332 s = Units::formatAngle(v, RS2::DegreesMinutesSeconds, 1);
1333 std::cout << "s: " << s << "\n";
1334 assert(s=="1� 29' 42\"");
1337 s = Units::formatAngle(v, RS2::DegreesMinutesSeconds, 1);
1338 std::cout << "s: " << s << "\n";
1339 assert(s=="1� 30' 0\"");