diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bf27190..9308aa02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,8 @@ New constraint features: in the text window. * When selecting an entity, the constraints applied to it can be selected in the text window. + * Distance constraint labels can now be formatted to use SI prefixes. + Values are edited in the configured unit regardless of label format. * It is now possible to turn off automatic creation of horizontal/vertical constraints on line segments. diff --git a/src/confscreen.cpp b/src/confscreen.cpp index 12b54375..6479fccc 100644 --- a/src/confscreen.cpp +++ b/src/confscreen.cpp @@ -69,6 +69,11 @@ void TextWindow::ScreenChangeDigitsAfterDecimalDegree(int link, uint32_t v) { SS.TW.edit.meaning = Edit::DIGITS_AFTER_DECIMAL_DEGREE; } +void TextWindow::ScreenChangeUseSIPrefixes(int link, uint32_t v) { + SS.useSIPrefixes = !SS.useSIPrefixes; + SS.GW.Invalidate(); +} + void TextWindow::ScreenChangeExportScale(int link, uint32_t v) { SS.TW.ShowEditControl(5, ssprintf("%.3f", (double)SS.exportScale)); SS.TW.edit.meaning = Edit::EXPORT_SCALE; @@ -239,15 +244,20 @@ void TextWindow::ShowConfiguration() { Printf(false, "%Ba %s %Fl%Ll%f%D[change]%E", SS.MmToString(SS.gridSpacing).c_str(), &ScreenChangeGridSpacing, 0); + + Printf(false, ""); Printf(false, "%Ft digits after decimal point to show%E"); Printf(false, "%Ba%Ft distances: %Fd%d %Fl%Ll%f%D[change]%E (e.g. '%s')", SS.UnitDigitsAfterDecimal(), &ScreenChangeDigitsAfterDecimal, 0, SS.MmToString(SS.StringToMm("1.23456789")).c_str()); - Printf(false, "%Ba%Ft angles: %Fd%d %Fl%Ll%f%D[change]%E (e.g. '%s')", + Printf(false, "%Bd%Ft angles: %Fd%d %Fl%Ll%f%D[change]%E (e.g. '%s')", SS.afterDecimalDegree, &ScreenChangeDigitsAfterDecimalDegree, 0, SS.DegreeToString(1.23456789).c_str()); + Printf(false, " %Fd%f%Ll%s use SI prefixes for distances%E", + &ScreenChangeUseSIPrefixes, + SS.useSIPrefixes ? CHECK_TRUE : CHECK_FALSE); Printf(false, ""); Printf(false, "%Ft export scale factor (1:1=mm, 1:25.4=inch)"); diff --git a/src/drawconstraint.cpp b/src/drawconstraint.cpp index 02aa6ab2..9beb9ead 100644 --- a/src/drawconstraint.cpp +++ b/src/drawconstraint.cpp @@ -18,13 +18,13 @@ std::string Constraint::Label() const { result = comment; } else if(type == Type::DIAMETER) { if(!other) { - result = "⌀" + SS.MmToString(valA); + result = "⌀" + SS.MmToStringSI(valA); } else { - result = "R" + SS.MmToString(valA / 2); + result = "R" + SS.MmToStringSI(valA / 2); } } else { // valA has units of distance - result = SS.MmToString(fabs(valA)); + result = SS.MmToStringSI(fabs(valA)); } if(reference) { result += " REF"; diff --git a/src/solvespace.cpp b/src/solvespace.cpp index 944ac431..c09ba03e 100644 --- a/src/solvespace.cpp +++ b/src/solvespace.cpp @@ -57,6 +57,7 @@ void SolveSpaceUI::Init() { afterDecimalMm = settings->ThawInt("AfterDecimalMm", 2); afterDecimalInch = settings->ThawInt("AfterDecimalInch", 3); afterDecimalDegree = settings->ThawInt("AfterDecimalDegree", 2); + useSIPrefixes = settings->ThawBool("UseSIPrefixes", false); // Camera tangent (determines perspective) cameraTangent = settings->ThawFloat("CameraTangent", 0.3f/1e3); // Grid spacing @@ -231,6 +232,7 @@ void SolveSpaceUI::Exit() { settings->FreezeInt("AfterDecimalMm", (uint32_t)afterDecimalMm); settings->FreezeInt("AfterDecimalInch", (uint32_t)afterDecimalInch); settings->FreezeInt("AfterDecimalDegree", (uint32_t)afterDecimalDegree); + settings->FreezeBool("UseSIPrefixes", useSIPrefixes); // Camera tangent (determines perspective) settings->FreezeFloat("CameraTangent", (float)cameraTangent); // Grid spacing @@ -307,7 +309,7 @@ double SolveSpaceUI::MmPerUnit() { } const char *SolveSpaceUI::UnitName() { switch(viewUnits) { - case Unit::INCHES: return "inch"; + case Unit::INCHES: return "in"; case Unit::METERS: return "m"; case Unit::MM: return "mm"; } @@ -315,13 +317,60 @@ const char *SolveSpaceUI::UnitName() { } std::string SolveSpaceUI::MmToString(double v) { + v /= MmPerUnit(); switch(viewUnits) { - case Unit::INCHES: return ssprintf("%.*f", afterDecimalInch, v / 25.4); - case Unit::METERS: return ssprintf("%.*f", afterDecimalMm, v / 1000.0); - case Unit::MM: return ssprintf("%.*f", afterDecimalMm, v); + case Unit::INCHES: + return ssprintf("%.*f", afterDecimalInch, v); + case Unit::METERS: + case Unit::MM: + return ssprintf("%.*f", afterDecimalMm, v); } return ""; } +static const char *DimToString(int dim) { + switch(dim) { + case 3: return "³"; + case 2: return "²"; + case 1: return ""; + default: ssassert(false, "Unexpected dimension"); + } +} +static std::pair SelectSIPrefixMm(int deg) { + if(deg >= 3) return { 3, "km" }; + else if(deg >= 0) return { 0, "m" }; + else if(deg >= -2) return { -2, "cm" }; + else if(deg >= -3) return { -3, "mm" }; + else if(deg >= -6) return { -6, "µm" }; + else return { -9, "nm" }; +} +static std::pair SelectSIPrefixInch(int deg) { + if(deg >= 0) return { 0, "in" }; + else if(deg >= -3) return { -3, "mil" }; + else return { -6, "µin" }; +} +std::string SolveSpaceUI::MmToStringSI(double v, int dim) { + bool compact = false; + if(dim == 0) { + if(!useSIPrefixes) return MmToString(v); + compact = true; + dim = 1; + } + + v /= pow((viewUnits == Unit::INCHES) ? 25.4 : 1000, dim); + int vdeg = floor((log10(fabs(v))) / dim); + std::string unit; + if(fabs(v) > 0.0) { + int sdeg = 0; + std::tie(sdeg, unit) = + (viewUnits == Unit::INCHES) + ? SelectSIPrefixInch(vdeg) + : SelectSIPrefixMm(vdeg); + v /= pow(10.0, sdeg * dim); + } + int pdeg = ceil(log10(fabs(v) + 1e-10)); + return ssprintf("%#.*g%s%s%s", pdeg + UnitDigitsAfterDecimal(), v, + compact ? "" : " ", unit.c_str(), DimToString(dim)); +} std::string SolveSpaceUI::DegreeToString(double v) { if(fabs(v - floor(v)) > 1e-10) { return ssprintf("%.*f", afterDecimalDegree, v); @@ -777,18 +826,11 @@ void SolveSpaceUI::MenuAnalyze(Command id) { vol += integral; } - - std::string msg = ssprintf(_("The volume of the solid model is:\n\n" - " %.3f %s^3"), - vol / pow(SS.MmPerUnit(), 3), - SS.UnitName()); - - if(SS.viewUnits == Unit::MM) { - msg += ssprintf("\n %.2f mL", vol/(10*10*10)); - } - msg += _("\n\nCurved surfaces have been approximated as triangles.\n" - "This introduces error, typically of around 1%."); - Message("%s", msg.c_str()); + Message(_("The volume of the solid model is:\n\n" + " %s\n\n" + "Curved surfaces have been approximated as triangles.\n" + "This introduces error, typically of around 1%%."), + SS.MmToStringSI(vol, /*dim=*/3).c_str()); break; } @@ -807,13 +849,11 @@ void SolveSpaceUI::MenuAnalyze(Command id) { sp.normal = sp.ComputeNormal(); sp.FixContourDirections(); double area = sp.SignedArea(); - double scale = SS.MmPerUnit(); Message(_("The area of the region sketched in this group is:\n\n" - " %.3f %s^2\n\n" + " %s\n\n" "Curves have been approximated as piecewise linear.\n" "This introduces error, typically of around 1%%."), - area / (scale*scale), - SS.UnitName()); + SS.MmToStringSI(area, /*dim=*/2).c_str()); sel.Clear(); sp.Clear(); break; @@ -829,14 +869,11 @@ void SolveSpaceUI::MenuAnalyze(Command id) { perimeter += e.b.Minus(e.a).Magnitude(); } } - - double scale = SS.MmPerUnit(); Message(_("The total length of the selected entities is:\n\n" - " %.3f %s\n\n" + " %s\n\n" "Curves have been approximated as piecewise linear.\n" "This introduces error, typically of around 1%%."), - perimeter / scale, - SS.UnitName()); + SS.MmToStringSI(perimeter, /*dim=*/1).c_str()); } else { Error(_("Bad selection for perimeter; select line segments, arcs, and curves.")); } diff --git a/src/solvespace.h b/src/solvespace.h index e23092ca..3fde8fbb 100644 --- a/src/solvespace.h +++ b/src/solvespace.h @@ -621,9 +621,11 @@ public: int afterDecimalMm; int afterDecimalInch; int afterDecimalDegree; + bool useSIPrefixes; int autosaveInterval; // in minutes std::string MmToString(double v); + std::string MmToStringSI(double v, int dim = 0); std::string DegreeToString(double v); double ExprToMm(Expr *e); double StringToMm(const std::string &s); diff --git a/src/ui.h b/src/ui.h index fd033d9d..a9d2acaf 100644 --- a/src/ui.h +++ b/src/ui.h @@ -469,6 +469,7 @@ public: static void ScreenChangeGridSpacing(int link, uint32_t v); static void ScreenChangeDigitsAfterDecimal(int link, uint32_t v); static void ScreenChangeDigitsAfterDecimalDegree(int link, uint32_t v); + static void ScreenChangeUseSIPrefixes(int link, uint32_t v); static void ScreenChangeExportScale(int link, uint32_t v); static void ScreenChangeExportOffset(int link, uint32_t v); static void ScreenChangeGCodeParameter(int link, uint32_t v);