Add a setting to format constraint labels using SI prefixes.

Supported metric units: km, m, cm, mm, µm, nm.
Supported USCS units: in, mil, µin.

Also, use the newly introduced unit formatting machinery in tools for
measuring perimeter, area and volume, so that e.g. volume is not
displayed in millions of cubic millimeters.
pull/434/head
EvilSpirit 2017-04-05 00:03:32 +07:00 committed by whitequark
parent 9faa7cb0ca
commit 9d1c295495
6 changed files with 81 additions and 29 deletions

View File

@ -33,6 +33,8 @@ New constraint features:
in the text window. in the text window.
* When selecting an entity, the constraints applied to it can be selected * When selecting an entity, the constraints applied to it can be selected
in the text window. 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 * It is now possible to turn off automatic creation of horizontal/vertical
constraints on line segments. constraints on line segments.

View File

@ -69,6 +69,11 @@ void TextWindow::ScreenChangeDigitsAfterDecimalDegree(int link, uint32_t v) {
SS.TW.edit.meaning = Edit::DIGITS_AFTER_DECIMAL_DEGREE; 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) { void TextWindow::ScreenChangeExportScale(int link, uint32_t v) {
SS.TW.ShowEditControl(5, ssprintf("%.3f", (double)SS.exportScale)); SS.TW.ShowEditControl(5, ssprintf("%.3f", (double)SS.exportScale));
SS.TW.edit.meaning = Edit::EXPORT_SCALE; SS.TW.edit.meaning = Edit::EXPORT_SCALE;
@ -239,15 +244,20 @@ void TextWindow::ShowConfiguration() {
Printf(false, "%Ba %s %Fl%Ll%f%D[change]%E", Printf(false, "%Ba %s %Fl%Ll%f%D[change]%E",
SS.MmToString(SS.gridSpacing).c_str(), SS.MmToString(SS.gridSpacing).c_str(),
&ScreenChangeGridSpacing, 0); &ScreenChangeGridSpacing, 0);
Printf(false, "");
Printf(false, "%Ft digits after decimal point to show%E"); 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')", Printf(false, "%Ba%Ft distances: %Fd%d %Fl%Ll%f%D[change]%E (e.g. '%s')",
SS.UnitDigitsAfterDecimal(), SS.UnitDigitsAfterDecimal(),
&ScreenChangeDigitsAfterDecimal, 0, &ScreenChangeDigitsAfterDecimal, 0,
SS.MmToString(SS.StringToMm("1.23456789")).c_str()); 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, SS.afterDecimalDegree,
&ScreenChangeDigitsAfterDecimalDegree, 0, &ScreenChangeDigitsAfterDecimalDegree, 0,
SS.DegreeToString(1.23456789).c_str()); 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, "");
Printf(false, "%Ft export scale factor (1:1=mm, 1:25.4=inch)"); Printf(false, "%Ft export scale factor (1:1=mm, 1:25.4=inch)");

View File

@ -18,13 +18,13 @@ std::string Constraint::Label() const {
result = comment; result = comment;
} else if(type == Type::DIAMETER) { } else if(type == Type::DIAMETER) {
if(!other) { if(!other) {
result = "" + SS.MmToString(valA); result = "" + SS.MmToStringSI(valA);
} else { } else {
result = "R" + SS.MmToString(valA / 2); result = "R" + SS.MmToStringSI(valA / 2);
} }
} else { } else {
// valA has units of distance // valA has units of distance
result = SS.MmToString(fabs(valA)); result = SS.MmToStringSI(fabs(valA));
} }
if(reference) { if(reference) {
result += " REF"; result += " REF";

View File

@ -57,6 +57,7 @@ void SolveSpaceUI::Init() {
afterDecimalMm = settings->ThawInt("AfterDecimalMm", 2); afterDecimalMm = settings->ThawInt("AfterDecimalMm", 2);
afterDecimalInch = settings->ThawInt("AfterDecimalInch", 3); afterDecimalInch = settings->ThawInt("AfterDecimalInch", 3);
afterDecimalDegree = settings->ThawInt("AfterDecimalDegree", 2); afterDecimalDegree = settings->ThawInt("AfterDecimalDegree", 2);
useSIPrefixes = settings->ThawBool("UseSIPrefixes", false);
// Camera tangent (determines perspective) // Camera tangent (determines perspective)
cameraTangent = settings->ThawFloat("CameraTangent", 0.3f/1e3); cameraTangent = settings->ThawFloat("CameraTangent", 0.3f/1e3);
// Grid spacing // Grid spacing
@ -231,6 +232,7 @@ void SolveSpaceUI::Exit() {
settings->FreezeInt("AfterDecimalMm", (uint32_t)afterDecimalMm); settings->FreezeInt("AfterDecimalMm", (uint32_t)afterDecimalMm);
settings->FreezeInt("AfterDecimalInch", (uint32_t)afterDecimalInch); settings->FreezeInt("AfterDecimalInch", (uint32_t)afterDecimalInch);
settings->FreezeInt("AfterDecimalDegree", (uint32_t)afterDecimalDegree); settings->FreezeInt("AfterDecimalDegree", (uint32_t)afterDecimalDegree);
settings->FreezeBool("UseSIPrefixes", useSIPrefixes);
// Camera tangent (determines perspective) // Camera tangent (determines perspective)
settings->FreezeFloat("CameraTangent", (float)cameraTangent); settings->FreezeFloat("CameraTangent", (float)cameraTangent);
// Grid spacing // Grid spacing
@ -307,7 +309,7 @@ double SolveSpaceUI::MmPerUnit() {
} }
const char *SolveSpaceUI::UnitName() { const char *SolveSpaceUI::UnitName() {
switch(viewUnits) { switch(viewUnits) {
case Unit::INCHES: return "inch"; case Unit::INCHES: return "in";
case Unit::METERS: return "m"; case Unit::METERS: return "m";
case Unit::MM: return "mm"; case Unit::MM: return "mm";
} }
@ -315,13 +317,60 @@ const char *SolveSpaceUI::UnitName() {
} }
std::string SolveSpaceUI::MmToString(double v) { std::string SolveSpaceUI::MmToString(double v) {
v /= MmPerUnit();
switch(viewUnits) { switch(viewUnits) {
case Unit::INCHES: return ssprintf("%.*f", afterDecimalInch, v / 25.4); case Unit::INCHES:
case Unit::METERS: return ssprintf("%.*f", afterDecimalMm, v / 1000.0); return ssprintf("%.*f", afterDecimalInch, v);
case Unit::MM: return ssprintf("%.*f", afterDecimalMm, v); case Unit::METERS:
case Unit::MM:
return ssprintf("%.*f", afterDecimalMm, v);
} }
return ""; 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<int, std::string> 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<int, std::string> 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) { std::string SolveSpaceUI::DegreeToString(double v) {
if(fabs(v - floor(v)) > 1e-10) { if(fabs(v - floor(v)) > 1e-10) {
return ssprintf("%.*f", afterDecimalDegree, v); return ssprintf("%.*f", afterDecimalDegree, v);
@ -777,18 +826,11 @@ void SolveSpaceUI::MenuAnalyze(Command id) {
vol += integral; vol += integral;
} }
Message(_("The volume of the solid model is:\n\n"
std::string msg = ssprintf(_("The volume of the solid model is:\n\n" " %s\n\n"
" %.3f %s^3"), "Curved surfaces have been approximated as triangles.\n"
vol / pow(SS.MmPerUnit(), 3), "This introduces error, typically of around 1%%."),
SS.UnitName()); SS.MmToStringSI(vol, /*dim=*/3).c_str());
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());
break; break;
} }
@ -807,13 +849,11 @@ void SolveSpaceUI::MenuAnalyze(Command id) {
sp.normal = sp.ComputeNormal(); sp.normal = sp.ComputeNormal();
sp.FixContourDirections(); sp.FixContourDirections();
double area = sp.SignedArea(); double area = sp.SignedArea();
double scale = SS.MmPerUnit();
Message(_("The area of the region sketched in this group is:\n\n" 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" "Curves have been approximated as piecewise linear.\n"
"This introduces error, typically of around 1%%."), "This introduces error, typically of around 1%%."),
area / (scale*scale), SS.MmToStringSI(area, /*dim=*/2).c_str());
SS.UnitName());
sel.Clear(); sel.Clear();
sp.Clear(); sp.Clear();
break; break;
@ -829,14 +869,11 @@ void SolveSpaceUI::MenuAnalyze(Command id) {
perimeter += e.b.Minus(e.a).Magnitude(); perimeter += e.b.Minus(e.a).Magnitude();
} }
} }
double scale = SS.MmPerUnit();
Message(_("The total length of the selected entities is:\n\n" 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" "Curves have been approximated as piecewise linear.\n"
"This introduces error, typically of around 1%%."), "This introduces error, typically of around 1%%."),
perimeter / scale, SS.MmToStringSI(perimeter, /*dim=*/1).c_str());
SS.UnitName());
} else { } else {
Error(_("Bad selection for perimeter; select line segments, arcs, and curves.")); Error(_("Bad selection for perimeter; select line segments, arcs, and curves."));
} }

View File

@ -621,9 +621,11 @@ public:
int afterDecimalMm; int afterDecimalMm;
int afterDecimalInch; int afterDecimalInch;
int afterDecimalDegree; int afterDecimalDegree;
bool useSIPrefixes;
int autosaveInterval; // in minutes int autosaveInterval; // in minutes
std::string MmToString(double v); std::string MmToString(double v);
std::string MmToStringSI(double v, int dim = 0);
std::string DegreeToString(double v); std::string DegreeToString(double v);
double ExprToMm(Expr *e); double ExprToMm(Expr *e);
double StringToMm(const std::string &s); double StringToMm(const std::string &s);

View File

@ -469,6 +469,7 @@ public:
static void ScreenChangeGridSpacing(int link, uint32_t v); static void ScreenChangeGridSpacing(int link, uint32_t v);
static void ScreenChangeDigitsAfterDecimal(int link, uint32_t v); static void ScreenChangeDigitsAfterDecimal(int link, uint32_t v);
static void ScreenChangeDigitsAfterDecimalDegree(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 ScreenChangeExportScale(int link, uint32_t v);
static void ScreenChangeExportOffset(int link, uint32_t v); static void ScreenChangeExportOffset(int link, uint32_t v);
static void ScreenChangeGCodeParameter(int link, uint32_t v); static void ScreenChangeGCodeParameter(int link, uint32_t v);