+//
+// units.cpp: Unit conversion/description
+//
+// Part of the Architektonas Project
+// (C) 2022 Underground Software
+// See the README and GPLv3 files for licensing and warranty information
+//
+// JLH = James Hammons <jlhamm@acm.org>
+//
+// WHO WHEN WHAT
+// --- ---------- ------------------------------------------------------------
+// JLH 01/04/2022 Created this file
+
+#include "units.h"
+
+QString GetDimensionText(Container * c, double length)
+{
+ QString dimText;
+
+ if (c->unitStyle == 0)
+ {
+ // Decimal style
+ int decPrecision = c->decimalPrecision + 1;
+
+ if (c->baseUnit == BUInch)
+ {
+ dimText = QString("%1\"").arg(length, 0, 'f', decPrecision);
+ }
+ else if (c->baseUnit == BUFoot)
+ {
+ double feet = (double)((int)length);
+ double inches = (length - feet) * 12.0;
+
+ QString ftText = (feet != 0 ? QString("%1'").arg(feet) : "");
+ QString inText = (inches != 0 ? QString(" %1\"").arg(inches, 0, 'f', decPrecision) : "");
+
+ dimText = QString("%1%2").arg(ftText).arg(inText).trimmed();
+ }
+ else if (c->baseUnit == BUYard)
+ {
+ double yards = (double)((int)length);
+ double feet = (length - yards) * 3.0;
+ double inches = (feet - ((int)feet)) * 12.0;
+ feet = (double)((int)feet); // Integerize the feet now...
+
+ QString ydText = (yards != 0 ? QString("%1 yd").arg(yards) : "");
+ QString ftText = (feet != 0 ? QString(" %1'").arg(feet) : "");
+ QString inText = (inches != 0 ? QString(" %1\"").arg(inches, 0, 'f', decPrecision) : "");
+
+ dimText = QString("%1%2%3")
+ .arg(ydText)
+ .arg(ftText)
+ .arg(inText)
+ .trimmed();
+ }
+ else // mile, mm, cm, m, km
+ {
+ dimText = QString("%1 %2")
+ .arg(length, 0, 'f', decPrecision)
+ .arg(buShortName[c->baseUnit]);
+ }
+ }
+ else
+ {
+ // Fractional style
+ int fracPrecision = 1 << (c->fractionalPrecision + 1);
+
+ if (c->baseUnit == BUInch)
+ {
+ dimText = MakeFraction(length, fracPrecision) + QChar('"');
+ }
+ else if (c->baseUnit == BUFoot)
+ {
+ double feet = (double)((int)length);
+ double inches = (length - feet) * 12.0;
+
+ QString ftText = (feet != 0 ? QString("%1'").arg(feet) : "");
+ QString inText = (inches != 0 ? MakeFraction(inches, fracPrecision) + QChar('"') : "");
+
+ dimText = QString("%1 %2").arg(ftText).arg(inText).trimmed();
+ }
+ else
+ {
+ dimText = QString("%1 %2").arg(MakeFraction(length, fracPrecision)).arg(buShortName[c->baseUnit]);
+ }
+ }
+
+ return dimText;
+}
+
+//
+// Create a string that represents the passed in double as a proper fraction
+// with the given precision. Fractions are rounded to the given precision
+// (which represents the denominator of the fraction).
+//
+QString MakeFraction(double num, int precision)
+{
+ int intPart = (int)num;
+ double fraction = num - (double)intPart;
+ int numerator = (int)(((double)precision * fraction) + 0.5);
+
+ // There's a chance we rounded up to the next whole number; if so, we fix
+ // that here.
+ if (numerator == precision)
+ {
+ intPart++;
+ numerator = 0;
+ }
+
+ // Reduce the fraction by casting out factors of two (we assume the
+ // precision will always be a power of two); that is, if there is a
+ // denominator to reduce.
+ if (numerator > 0)
+ {
+ // Divide fraction by two while the denominator is not odd. N.B.: This
+ // will always end, as any non-zero number will eventually have a 1-bit
+ // in it somewhere, which we shift down to the low bit position with
+ // each iteration.
+ while ((numerator & 0x01) == 0)
+ {
+ numerator /= 2;
+ precision /= 2;
+ }
+ }
+
+ QString intText = (intPart != 0 ? QString("%1").arg(intPart) : "");
+ QString fracText = (numerator != 0 ? QString(" %1/%2").arg(numerator).arg(precision) : "");
+
+ return QString("%1%2").arg(intText).arg(fracText).trimmed();
+}