1 /****************************************************************************
2 ** $Id: rs_units.cpp 1938 2004-12-09 23:09:53Z andrew $
4 ** Copyright (C) 2001-2003 RibbonSoft. All rights reserved.
6 ** This file is part of the qcadlib Library project.
8 ** This file may be distributed and/or modified under the terms of the
9 ** GNU General Public License version 2 as published by the Free Software
10 ** Foundation and appearing in the file LICENSE.GPL included in the
11 ** packaging of this file.
13 ** Licensees holding valid qcadlib Professional Edition licenses may use
14 ** this file in accordance with the qcadlib Commercial License
15 ** Agreement provided with the Software.
17 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
18 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20 ** See http://www.ribbonsoft.com for further details.
22 ** Contact info@ribbonsoft.com if any conditions of this licensing are
25 **********************************************************************/
34 * Converts a DXF integer () to a Unit enum.
36 RS2::Unit RS_Units::dxfint2unit(int dxfint)
38 return (RS2::Unit)dxfint;
51 return RS2::Millimeter;
53 return RS2::Centimeter;
57 return RS2::Kilometer;
59 return RS2::Microinch;
67 return RS2::Nanometer;
71 return RS2::Decimeter;
73 return RS2::Decameter;
75 return RS2::Hectometer;
77 return RS2::Gigameter;
81 return RS2::Lightyear;
88 * @return a short string representing the given unit (e.g. "mm")
90 QString RS_Units::unitToSign(RS2::Unit u)
108 case RS2::Millimeter:
111 case RS2::Centimeter:
144 case RS2::Hectometer:
169 * @return a string representing the given unit (e.g. "Millimeter").
170 * translated if @a t is true (the default).
172 QString RS_Units::unitToString(RS2::Unit u, bool t)
179 ret = t ? QObject::tr("None") : QString("None");
182 ret = t ? QObject::tr("Inch") : QString("Inch");
185 ret = t ? QObject::tr("Foot") : QString("Foot");
188 ret = t ? QObject::tr("Mile") : QString("Mile");
190 case RS2::Millimeter:
191 ret = t ? QObject::tr("Millimeter") : QString("Millimeter");
193 case RS2::Centimeter:
194 ret = t ? QObject::tr("Centimeter") : QString("Centimeter");
197 ret = t ? QObject::tr("Meter") : QString("Meter");
200 ret = t ? QObject::tr("Kilometer") : QString("Kilometer");
203 ret = t ? QObject::tr("Microinch") : QString("Microinch");
206 ret = t ? QObject::tr("Mil") : QString("Mil");
209 ret = t ? QObject::tr("Yard") : QString("Yard");
212 ret = t ? QObject::tr("Angstrom") : QString("Angstrom");
215 ret = t ? QObject::tr("Nanometer") : QString("Nanometer");
218 ret = t ? QObject::tr("Micron") : QString("Micron");
221 ret = t ? QObject::tr("Decimeter") : QString("Decimeter");
224 ret = t ? QObject::tr("Decameter") : QString("Decameter");
226 case RS2::Hectometer:
227 ret = t ? QObject::tr("Hectometer") : QString("Hectometer");
230 ret = t ? QObject::tr("Gigameter") : QString("Gigameter");
233 ret = t ? QObject::tr("Astro") : QString("Astro");
236 ret = t ? QObject::tr("Lightyear") : QString("Lightyear");
239 ret = t ? QObject::tr("Parsec") : QString("Parsec");
251 * Converts a string into a unit enum.
253 RS2::Unit RS_Units::stringToUnit(const QString & u)
255 RS2::Unit ret = RS2::None;
261 else if (u == QObject::tr("Inch"))
265 else if (u==QObject::tr("Foot"))
269 else if (u==QObject::tr("Mile"))
273 else if (u==QObject::tr("Millimeter"))
275 ret = RS2::Millimeter;
277 else if (u==QObject::tr("Centimeter"))
279 ret = RS2::Centimeter;
281 else if (u==QObject::tr("Meter"))
285 else if (u==QObject::tr("Kilometer"))
287 ret = RS2::Kilometer;
289 else if (u==QObject::tr("Microinch"))
291 ret = RS2::Microinch;
293 else if (u==QObject::tr("Mil"))
297 else if (u==QObject::tr("Yard"))
301 else if (u==QObject::tr("Angstrom"))
305 else if (u==QObject::tr("Nanometer"))
307 ret = RS2::Nanometer;
309 else if (u==QObject::tr("Micron"))
313 else if (u==QObject::tr("Decimeter"))
315 ret = RS2::Decimeter;
317 else if (u==QObject::tr("Decameter"))
319 ret = RS2::Decameter;
321 else if (u==QObject::tr("Hectometer"))
323 ret = RS2::Hectometer;
325 else if (u==QObject::tr("Gigameter"))
327 ret = RS2::Gigameter;
329 else if (u==QObject::tr("Astro"))
333 else if (u==QObject::tr("Lightyear"))
335 ret = RS2::Lightyear;
337 else if (u==QObject::tr("Parsec"))
346 * @return true: the unit is metric, false: the unit is imperial.
348 bool RS_Units::isMetric(RS2::Unit u)
350 if (u == RS2::Millimeter || u == RS2::Centimeter || u == RS2::Meter
351 || u == RS2::Kilometer || u == RS2::Angstrom || u == RS2::Nanometer
352 || u == RS2::Micron || u == RS2::Decimeter || u == RS2::Decameter
353 || u == RS2::Hectometer || u == RS2::Gigameter || u == RS2::Astro
354 || u == RS2::Lightyear || u == RS2::Parsec)
361 * @return factor to convert the given unit to Millimeters.
363 double RS_Units::getFactorToMM(RS2::Unit u)
381 case RS2::Millimeter:
384 case RS2::Centimeter:
417 case RS2::Hectometer:
424 ret = 149600000000000.0;
427 ret = 9460731798000000000.0;
430 ret = 30857000000000000000.0;
441 * Converts the given value 'val' from unit 'src' to unit 'dest'.
443 double RS_Units::convert(double val, RS2::Unit src, RS2::Unit dest)
445 if (getFactorToMM(dest) > 0.0)
447 return (val * getFactorToMM(src)) / getFactorToMM(dest);
451 RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Units::convert: invalid factor");
457 * Converts the given vector 'val' from unit 'src' to unit 'dest'.
459 Vector RS_Units::convert(const Vector val, RS2::Unit src, RS2::Unit dest)
461 return Vector(convert(val.x, src, dest), convert(val.y, src, dest), convert(val.z, src, dest));
465 * Formats the given length in the given format.
467 * @param length The length in the current unit of the drawing.
468 * @param format Format of the string.
469 * @param prec Precisision of the value (e.g. 0.001 or 1/128 = 0.0078125)
470 & @param showUnit Append unit to the value.
472 QString RS_Units::formatLinear(double length, RS2::Unit unit, RS2::LinearFormat format,
473 int prec, bool showUnit)
477 // unit appended to value (e.g. 'mm'):
478 /*QString unitString = "";
480 unitString = unitToSign(unit);
483 // barbarian display: show as fraction:
486 case RS2::Scientific:
487 ret = formatScientific(length, unit, prec, showUnit);
491 ret = formatDecimal(length, unit, prec, showUnit);
494 case RS2::Engineering:
495 ret = formatEngineering(length, unit, prec, showUnit);
498 case RS2::Architectural:
499 ret = formatArchitectural(length, unit, prec, showUnit);
502 case RS2::Fractional:
503 ret = formatFractional(length, unit, prec, showUnit);
507 RS_DEBUG->print(RS_Debug::D_WARNING,
508 "RS_Units::formatLinear: Unknown format");
517 * Formats the given length in scientific format (e.g. 2.5E7).
519 * @param length The length in the current unit of the drawing.
520 * @param prec Precisision of the value (e.g. 0.001 or 1/128 = 0.0078125)
521 & @param showUnit Append unit to the value.
523 QString RS_Units::formatScientific(double length, RS2::Unit unit, int prec, bool showUnit)
527 // unit appended to value (e.g. 'mm'):
528 QString unitString = "";
531 unitString = unitToSign(unit);
534 sprintf(format, "%%.%dE%%s", prec);
535 ret.sprintf(format, length, (const char *)unitString.toLocal8Bit());
541 * Formats the given length in decimal (normal) format (e.g. 2.5).
543 * @param length The length in the current unit of the drawing.
544 * @param prec Precisision of the value (e.g. 0.001)
545 & @param showUnit Append unit to the value.
547 QString RS_Units::formatDecimal(double length, RS2::Unit unit, int prec, bool showUnit)
551 // unit appended to value (e.g. 'mm'):
552 QString unitString = "";
555 unitString = unitToSign(unit);
557 ret = RS_Math::doubleToString(length, prec);
566 * Formats the given length in engineering format (e.g. 5' 4.5").
568 * @param length The length in the current unit of the drawing.
569 * @param prec Precisision of the value (e.g. 0.001 or 1/128 = 0.0078125)
570 & @param showUnit Append unit to the value.
572 QString RS_Units::formatEngineering(double length, RS2::Unit /*unit*/, int prec, bool /*showUnit*/)
576 bool sign = (length < 0.0);
577 int feet = (int)floor(fabs(length) / 12);
578 double inches = fabs(length) - feet * 12;
580 QString sInches = RS_Math::doubleToString(inches, prec);
590 ret.sprintf("%d'-%s\"", feet, (const char *)sInches.toLocal8Bit());
594 ret.sprintf("%s\"", (const char *)sInches.toLocal8Bit());
604 * Formats the given length in architectural format (e.g. 5' 4 1/2").
606 * @param length The length in the current unit of the drawing.
607 * @param prec Precisision of the value (e.g. 0.001 or 1/128 = 0.0078125)
608 & @param showUnit Append unit to the value.
610 QString RS_Units::formatArchitectural(double length, RS2::Unit /*unit*/, int prec, bool showUnit)
613 bool neg = (length < 0.0);
614 int feet = (int)floor(fabs(length) / 12);
615 double inches = fabs(length) - feet * 12;
617 QString sInches = formatFractional(inches, RS2::Inch, prec, showUnit);
627 ret.sprintf("-%d'-%s\"", feet, (const char *)sInches.toLocal8Bit());
631 ret.sprintf("%d'-%s\"", feet, (const char *)sInches.toLocal8Bit());
638 * Formats the given length in fractional (barbarian) format (e.g. 5' 3 1/64").
640 * @param length The length in the current unit of the drawing.
641 * @param unit Should be inches.
642 * @param prec Precisision of the value (e.g. 0.001 or 1/128 = 0.0078125)
643 & @param showUnit Append unit to the value.
645 QString RS_Units::formatFractional(double length, RS2::Unit /*unit*/, int prec, bool /*showUnit*/)
649 int num; // number of complete inches (num' 7/128")
650 int nominator; // number of fractions (nominator/128)
651 int denominator; // (4/denominator)
659 length = fabs(length);
662 num = (int)floor(length);
664 denominator = (int)RS_Math::pow(2, prec);
665 nominator = RS_Math::round((length - num) * denominator);
667 // fraction rounds up to 1:
668 if (nominator == denominator)
675 // Simplify the fraction
676 if (nominator != 0 && denominator != 0)
678 int gcd = RS_Math::findGCD(nominator, denominator);
682 nominator = nominator / gcd;
683 denominator = denominator / gcd;
687 RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Units::formatFractional: invalid gcd");
693 if (num != 0 && nominator != 0)
695 ret.sprintf("%s%d %d/%d", (const char *)neg.toLocal8Bit(), num, nominator, denominator);
697 else if (nominator != 0)
699 ret.sprintf("%s%d/%d", (const char *)neg.toLocal8Bit(), nominator, denominator);
703 ret.sprintf("%s%d", (const char *)neg.toLocal8Bit(), num);
714 * Formats the given angle with the given format.
716 * @param angle The angle (always in rad).
717 * @param format Format of the string.
718 * @param prec Precisision of the value (e.g. 0.001 or 1/128 = 0.0078125)
720 * @ret String with the formatted angle.
722 QString RS_Units::formatAngle(double angle, RS2::AngleFormat format, int prec)
729 case RS2::DegreesDecimal:
730 case RS2::DegreesMinutesSeconds:
731 value = RS_Math::rad2deg(angle);
737 value = RS_Math::rad2gra(angle);
740 RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Units::formatAngle: Unknown Angle Unit");
747 case RS2::DegreesDecimal:
750 ret = RS_Math::doubleToString(value, prec);
752 if (format == RS2::DegreesDecimal)
754 if (format == RS2::Radians)
756 if (format == RS2::Gradians)
760 case RS2::DegreesMinutesSeconds:
762 int vDegrees, vMinutes;
764 QString degrees, minutes, seconds;
766 vDegrees = (int)floor(value);
767 vMinutes = (int)floor((value - vDegrees) * 60.0);
768 vSeconds = (value - vDegrees - (vMinutes / 60.0)) * 3600.0;
770 seconds = RS_Math::doubleToString(vSeconds, (prec > 1 ? prec - 2 : 0));
784 if (prec == 0 && vMinutes >= 30.0)
788 else if (prec == 1 && vSeconds >= 30.0)
793 degrees.setNum(vDegrees);
794 minutes.setNum(vMinutes);
799 ret = degrees + QChar(0xB0);
802 ret = degrees + QChar(0xB0) + " " + minutes + "'";
805 ret = degrees + QChar(0xB0) + " " + minutes + "' " + seconds + "\"";
819 * @return Size of the given paper format.
821 Vector RS_Units::paperFormatToSize(RS2::PaperFormat p)
828 ret = Vector(0.0, 0.0);
831 ret = Vector(215.9, 279.4);
834 ret = Vector(215.9, 355.6);
837 ret = Vector(190.5, 254.0);
840 ret = Vector(841.0, 1189.0);
843 ret = Vector(594.0, 841.0);
846 ret = Vector(420.0, 594.0);
849 ret = Vector(297.0, 420.0);
852 ret = Vector(210.0, 297.0);
855 ret = Vector(148.0, 210.0);
858 ret = Vector(105.0, 148.0);
861 ret = Vector(74.0, 105.0);
864 ret = Vector(52.0, 74.0);
867 ret = Vector(37.0, 52.0);
870 ret = Vector(26.0, 37.0);
873 ret = Vector(1000.0, 1414.0);
876 ret = Vector(707.0, 1000.0);
879 ret = Vector(500.0, 707.0);
882 ret = Vector(353.0, 500.0);
885 ret = Vector(250.0, 353.0);
888 ret = Vector(176.0, 250.0);
891 ret = Vector(125.0, 176.0);
894 ret = Vector(88.0, 125.0);
897 ret = Vector(62.0, 88.0);
900 ret = Vector(44.0, 62.0);
903 ret = Vector(31.0, 44.0);
907 ret = Vector(917.0, 1297.0);
910 ret = Vector(648.0, 917.0);
913 ret = Vector(458.0, 648.0);
916 ret = Vector(324.0, 458.0);
919 ret = Vector(229.0, 324.0);
922 ret = Vector(162.0, 229.0);
925 ret = Vector(114.0, 162.0);
928 ret = Vector(81.0, 114.0);
931 ret = Vector(57.0, 81.0);
934 ret = Vector(40.0, 57.0);
937 ret = Vector(28.0, 40.0);
941 ret = Vector(163.0, 229.0);
944 ret = Vector(105.0, 241.0);
947 ret = Vector(110.0, 220.0);
950 ret = Vector(210.0, 330.0);
953 // ret = Vector(432.0, 279.0);
956 ret = Vector(279.0, 432.0);
959 ret = Vector(0.0, 0.0);
969 * Gets the paper format which matches the given size. If no
970 * format matches, RS2::Custom is returned.
972 RS2::PaperFormat RS_Units::paperSizeToFormat(const Vector s)
977 for(int i=(int)RS2::Custom; i<=(int)RS2::NPageSize; ++i)
979 ts1 = RS_Units::paperFormatToSize((RS2::PaperFormat)i);
980 ts2 = Vector(ts1.y, ts1.x);
982 if (ts1.distanceTo(s) < 1.0e-4 || ts2.distanceTo(s) < 1.0e-4)
983 return (RS2::PaperFormat)i;
990 * Converts a paper format to a string (e.g. for a combobox).
992 QString RS_Units::paperFormatToString(RS2::PaperFormat p)
1007 case RS2::Executive:
1126 case RS2::NPageSize:
1137 * Converts a string to a paper format.
1139 RS2::PaperFormat RS_Units::stringToPaperFormat(const QString & p)
1141 QString ls = p.toLower();
1142 RS2::PaperFormat ret = RS2::Custom;
1146 else if (p == "letter")
1148 else if (p == "legal")
1150 else if (p == "executive")
1151 ret = RS2::Executive;
1192 else if (p == "b10")
1194 /*else if (p=="c0") {
1196 } else if (p=="c1") {
1198 } else if (p=="c2") {
1200 } else if (p=="c3") {
1202 } else if (p=="c4") {
1204 } else if (p=="c5") {
1206 } else if (p=="c6") {
1208 } else if (p=="c7") {
1210 } else if (p=="c8") {
1212 } else if (p=="c9") {
1214 } else if (p=="c10") {
1217 else if (p == "c5e")
1219 else if (p == "comm10e")
1221 else if (p == "dle")
1223 else if (p == "folio")
1225 //else if (p=="ledger")
1226 // ret = RS2::Ledger;
1227 else if (p == "tabloid")
1229 else if (p == "npagesize")
1230 ret = RS2::NPageSize;
1236 * Performs some testing for the math class.
1238 void RS_Units::test()
1244 std::cout << "RS_Units::test: formatLinear (decimal):\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";
1266 s = RS_Units::formatLinear(v, RS2::Millimeter, RS2::Decimal,
1268 std::cout << "s: " << s << "\n";
1271 s = RS_Units::formatLinear(v, RS2::Millimeter, RS2::Decimal,
1273 std::cout << "s: " << s << "\n";
1277 s = RS_Units::formatLinear(v, RS2::Millimeter, RS2::Decimal,
1279 std::cout << "s: " << s << "\n";
1283 std::cout << "RS_Units::test: formatLinear (fractional):\n";
1285 s = RS_Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1287 std::cout << "s: " << s << "\n";
1288 assert(s=="1 13/64");
1291 s = RS_Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1293 std::cout << "s: " << s << "\n";
1294 assert(s=="1 51/256");
1297 s = RS_Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1299 std::cout << "s: " << s << "\n";
1300 assert(s=="51/256");
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 s = RS_Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1329 std::cout << "s: " << s << "\n";
1333 s = RS_Units::formatLinear(v, RS2::Inch, RS2::Fractional,
1335 std::cout << "s: " << s << "\n";
1339 for(v=11.9999; v<12.0001; v+=0.0000001)
1341 for(int prec=0; prec<=6; ++prec)
1343 s = RS_Units::formatLinear(v, RS2::Inch, RS2::Architectural, prec, true);
1344 std::cout << "prec: " << prec << " v: " << v << " s: " << s.toAscii().data() << "\n";
1348 /*for (v=0.0; v<10.0; v+=0.001) {
1349 s = RS_Units::formatLinear(v, RS2::Foot, RS2::Fractional,
1351 std::cout << "v: " << v << " s: " << s << "\n";
1355 std::cout << "RS_Units::test: formatLinear (scientific):\n";
1357 s = RS_Units::formatLinear(v, RS2::Millimeter, RS2::Scientific,
1359 std::cout << "s: " << s << "\n";
1360 assert(s=="1.0e-3");
1365 std::cout << "RS_Units::test: formatAngle (deg / decimal):\n";
1367 s = RS_Units::formatAngle(v, RS2::DegreesDecimal, 2);
1368 std::cout << "s: " << s << "\n";
1372 s = RS_Units::formatAngle(v, RS2::DegreesDecimal, 2);
1373 std::cout << "s: " << s << "\n";
1377 s = RS_Units::formatAngle(v, RS2::DegreesDecimal, 2);
1378 std::cout << "s: " << s << "\n";
1381 std::cout << "RS_Units::test: formatAngle (deg / d/m/s):\n";
1384 s = RS_Units::formatAngle(v, RS2::DegreesMinutesSeconds, 1);
1385 std::cout << "s: " << s << "\n";
1386 assert(s=="1� 29' 42\"");
1389 s = RS_Units::formatAngle(v, RS2::DegreesMinutesSeconds, 1);
1390 std::cout << "s: " << s << "\n";
1391 assert(s=="1� 30' 0\"");