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.
This commit is contained in:
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.
* 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.

View File

@ -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)");

View File

@ -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";

View File

@ -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<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) {
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."));
}

View File

@ -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);

View File

@ -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);