IMPROVED: axis labeling: there were some minor differences between compilers

IMPROVED: jkqtp_floattounitstr()/jkqtp_floattolatexunitstr(): add all SI-Prefixes from 10^-30...10^30
BREAKING removed unused function variant of jkqtp_floattounitstr()
NEW Added unit test for jkqtp_floattounitstr()/jkqtp_floattolatexunitstr()
This commit is contained in:
jkriege2 2024-02-02 14:24:41 +01:00
parent 55625a25ee
commit 57b298527e
5 changed files with 111 additions and 47 deletions

View File

@ -75,6 +75,9 @@ Changes, compared to \ref page_whatsnew_V4_0_0 "v4.0.0" include:
<li>IMPROVED/REWORKED: The functions JKQTBasePlotter::saveImage(), JKQTBasePlotter::saveAsPixelImage(), JKQTBasePlotter::saveAsPDF(), JKQTBasePlotter::saveSVG(), ... gained a bool return value to indicate whether sacing was successful.</li> <li>IMPROVED/REWORKED: The functions JKQTBasePlotter::saveImage(), JKQTBasePlotter::saveAsPixelImage(), JKQTBasePlotter::saveAsPDF(), JKQTBasePlotter::saveSVG(), ... gained a bool return value to indicate whether sacing was successful.</li>
<li>IMPROVED/REWORKED: More <code>save...()</code> functions will appear in the API of JKQTPlotter, so you don't have to go via JKQTPlotter::getPlotter(). These are merely forwarding the call to the internel JKQTBasePlotter instance.</li> <li>IMPROVED/REWORKED: More <code>save...()</code> functions will appear in the API of JKQTPlotter, so you don't have to go via JKQTPlotter::getPlotter(). These are merely forwarding the call to the internel JKQTBasePlotter instance.</li>
<li>IMPROVED: documentation of styles: automatized doc image generation.</li> <li>IMPROVED: documentation of styles: automatized doc image generation.</li>
<li>IMPROVED: axis labeling: there were some minor differences between compilers</li>
<li>IMPROVED: jkqtp_floattounitstr()/jkqtp_floattolatexunitstr(): add all SI-Prefixes from 10^-30...10^30</li>
<li>BREAKING removed unused function variant of jkqtp_floattounitstr()</li>
<li>NEW: JKQTPFilledCurveXGraph and JKQTPFilledCurveYGraph can now plot wiggle plots with different fill styles above and below the baseline (feature request <a href="https://github.com/jkriege2/JKQtPlotter/issues/68">#68 Wiggle Plots</a> from <a href="https://github.com/xichaoqiang">user:xichaoqiang</a> </li> <li>NEW: JKQTPFilledCurveXGraph and JKQTPFilledCurveYGraph can now plot wiggle plots with different fill styles above and below the baseline (feature request <a href="https://github.com/jkriege2/JKQtPlotter/issues/68">#68 Wiggle Plots</a> from <a href="https://github.com/xichaoqiang">user:xichaoqiang</a> </li>
<li>NEW/BREAKING CHANGE: data tooltip can now also be shown when "just" moving the mouse (so far this was only possible when dragging the mouse with a button pressed). This also removes JKQtPlotter::getActMouseLeftAsToolTip() and adds JKQtPlotter::getActMouseMoveToolTip() instead! Also the default toolbars and context menus changed!</li> <li>NEW/BREAKING CHANGE: data tooltip can now also be shown when "just" moving the mouse (so far this was only possible when dragging the mouse with a button pressed). This also removes JKQtPlotter::getActMouseLeftAsToolTip() and adds JKQtPlotter::getActMouseMoveToolTip() instead! Also the default toolbars and context menus changed!</li>
<li>NEW: new "seaborn" style for plots, see \ref jkqtpplotter_styling </li> <li>NEW: new "seaborn" style for plots, see \ref jkqtpplotter_styling </li>

View File

@ -156,42 +156,49 @@ std::string jkqtp_tolower(const std::string& s){
return r; return r;
} }
std::string jkqtp_floattounitstr(double dataa, const std::string& unitname){
if (dataa==0) return jkqtp_floattostr(dataa)+unitname;
std::string u="";
double factor=1;
double data=fabs(dataa);
if (data>=1e3) { u="k"; factor=1e3; }
if (data>=1e6) { u="M"; factor=1e6; }
if (data>=1e9) { u="G"; factor=1e9; }
if (data>=1e12) { u="T"; factor=1e12; }
if (data>=1e15) { u="P"; factor=1e15; }
if (data>=1e18) { u="E"; factor=1e18; }
if (data<1) {u="m"; factor=1e-3; }
if (data<1e-3) {u="u"; factor=1e-6; }
if (data<1e-6) {u="n"; factor=1e-9; }
if (data<1e-9) {u="p"; factor=1e-12; }
if (data<1e-12) {u="f"; factor=1e-15; }
if (data<1e-15) {u="a"; factor=1e-18; }
return jkqtp_floattostr(dataa/factor)+u+unitname;
};
std::string jkqtp_floattounitstr(double data, int past_comma, bool remove_trail0, double belowIsZero){ std::string jkqtp_floattounitstr(double data, int past_comma, bool remove_trail0, double belowIsZero){
if (fabs(data)<=belowIsZero) return "0"; if (fabs(data)<=belowIsZero) return "0";
std::string form="%."+jkqtp_inttostr(past_comma)+"lf"; std::string form="%."+jkqtp_inttostr(past_comma)+"lf";
std::string res=jkqtp_format(form,data); std::string res=jkqtp_format(form,data);
std::string unit=""; std::string unit="";
if (fabs(data)>=1e3) {res=jkqtp_format(form,data/1e3); unit="k";} static std::map<double, std::string> SIUnits = {
if (fabs(data)>=1e6) {res=jkqtp_format(form,data/1e6); unit="M";} {1e3, "k"},
if (fabs(data)>=1e9) {res=jkqtp_format(form,data/1e9); unit="G";} {1e6, "M"},
if (fabs(data)>=1e12) {res=jkqtp_format(form,data/1e12); unit="T";} {1e9, "G"},
if (fabs(data)>=1e15) {res=jkqtp_format(form,data/1e15); unit="P";} {1e12, "T"},
if (fabs(data)<1) {res=jkqtp_format(form,data/1e-3); unit="m";} {1e15, "P"},
if (fabs(data)<1e-3) {res=jkqtp_format(form,data/1e-6); unit="u";} {1e18, "E"},
if (fabs(data)<1e-6) {res=jkqtp_format(form,data/1e-9); unit="n";} {1e21, "Z"},
if (fabs(data)<1e-9) {res=jkqtp_format(form,data/1e-12); unit="f";} {1e24, "Y"},
{1e27, "R"},
{1e30, "Q"},
};
static std::map<double, std::string> SIUnitsBelow1 = {
{1e-3, "m"},
{1e-6, "\xB5"},
{1e-9, "n"},
{1e-12, "p"},
{1e-15, "f"},
{1e-18, "a"},
{1e-21, "z"},
{1e-24, "y"},
{1e-27, "r"},
{1e-30, "q"},
};
const double absData=fabs(data);
for (auto it=SIUnits.begin(); it!=SIUnits.end(); it++) {
if (absData>=it->first) {
res=jkqtp_format(form,data/it->first);
unit=it->second;
}
}
for (auto it=SIUnitsBelow1.rbegin(); it!=SIUnitsBelow1.rend(); it++) {
if (absData<it->first*1e3) {
res=jkqtp_format(form,data/it->first);
unit=it->second;
}
}
if (fabs(data)==0) res=jkqtp_format(form,data); if (fabs(data)==0) res=jkqtp_format(form,data);
if (remove_trail0) { if (remove_trail0) {
if (fabs(data)<=belowIsZero) return "0"; if (fabs(data)<=belowIsZero) return "0";
@ -212,16 +219,43 @@ std::string jkqtp_tolower(const std::string& s){
std::string form="%."+jkqtp_inttostr(past_comma)+"lf"; std::string form="%."+jkqtp_inttostr(past_comma)+"lf";
std::string res=jkqtp_format(form,data); std::string res=jkqtp_format(form,data);
std::string unit=""; std::string unit="";
if (fabs(data)>=1e3) {res=jkqtp_format(form,data/1e3); unit="\\mathrm{k}";} static std::map<double, std::string> SIUnits = {
if (fabs(data)>=1e6) {res=jkqtp_format(form,data/1e6); unit="\\;\\mathrm{M}";} {1e3, "k"},
if (fabs(data)>=1e9) {res=jkqtp_format(form,data/1e9); unit="\\;\\mathrm{G}";} {1e6, "M"},
if (fabs(data)>=1e12) {res=jkqtp_format(form,data/1e12); unit="\\;\\mathrm{T}";} {1e9, "G"},
if (fabs(data)>=1e15) {res=jkqtp_format(form,data/1e15); unit="\\;\\mathrm{P}";} {1e12, "T"},
if (fabs(data)<1) {res=jkqtp_format(form,data/1e-3); unit="\\;\\mathrm{m}";} {1e15, "P"},
if (fabs(data)<1e-3) {res=jkqtp_format(form,data/1e-6); unit="\\;\\mathrm{\\mu}";} {1e18, "E"},
if (fabs(data)<1e-6) {res=jkqtp_format(form,data/1e-9); unit="\\;\\mathrm{n}";} {1e21, "Z"},
if (fabs(data)<1e-9) {res=jkqtp_format(form,data/1e-12); unit="\\;\\mathrm{f}";} {1e24, "Y"},
if (fabs(data)==0) res=jkqtp_format(form,data); {1e27, "R"},
{1e30, "Q"},
};
static std::map<double, std::string> SIUnitsBelow1 = {
{1e-3, "m"},
{1e-6, "\\mu"},
{1e-9, "n"},
{1e-12, "p"},
{1e-15, "f"},
{1e-18, "a"},
{1e-21, "z"},
{1e-24, "y"},
{1e-27, "r"},
{1e-30, "q"},
};
const double absData=fabs(data);
for (auto it=SIUnits.begin(); it!=SIUnits.end(); it++) {
if (absData>=it->first) {
res=jkqtp_format(form,data/it->first);
unit="\\;\\mathrm{"+it->second+"}";
}
}
for (auto it=SIUnitsBelow1.rbegin(); it!=SIUnitsBelow1.rend(); it++) {
if (absData<it->first*1e3) {
res=jkqtp_format(form,data/it->first);
unit="\\;\\mathrm{"+it->second+"}";
}
}
if (remove_trail0) { if (remove_trail0) {
if (fabs(data)<=belowIsZero) return "0"; if (fabs(data)<=belowIsZero) return "0";
if (res.find('.')==std::string::npos) return res+unit; if (res.find('.')==std::string::npos) return res+unit;
@ -250,7 +284,7 @@ std::string jkqtp_tolower(const std::string& s){
long exp=static_cast<long>(floor(log(adata)/JKQTPSTATISTICS_LN10)); long exp=static_cast<long>(floor(log(adata)/JKQTPSTATISTICS_LN10));
if ((minNoExponent>fabs(data)) || (fabs(data)>maxNoExponent)) { if ((minNoExponent>fabs(data)) || (fabs(data)>maxNoExponent)) {
std::string v=jkqtp_floattostr(data/pow(10.0, static_cast<double>(exp)), past_comma, remove_trail0); const std::string v=jkqtp_floattostr(data/pow(10.0, static_cast<double>(exp)), past_comma, remove_trail0);
if (v!="1" && v!="10") { if (v!="1" && v!="10") {
res=v+std::string("{")+multOperator+std::string("}10^{")+jkqtp_inttostr(exp)+"}"; res=v+std::string("{")+multOperator+std::string("}10^{")+jkqtp_inttostr(exp)+"}";
} else { } else {

View File

@ -188,10 +188,6 @@ JKQTCOMMON_LIB_EXPORT std::string jkqtp_uinttostr(unsigned long data);
*/ */
JKQTCOMMON_LIB_EXPORT std::string jkqtp_floattostr(double data, int past_comma=-1, bool remove_trail0=false, double belowIsZero=1e-16); JKQTCOMMON_LIB_EXPORT std::string jkqtp_floattostr(double data, int past_comma=-1, bool remove_trail0=false, double belowIsZero=1e-16);
/** \brief convert a double to a string, encoding powers of ten as characters, e.g. \c jkqtp_floattounitstr(1000,"g") will result in "1kg"
* \ingroup jkqtptools_string
*/
JKQTCOMMON_LIB_EXPORT std::string jkqtp_floattounitstr(double dataa, const std::string& unitname);
/** \brief convert a boolean to a string /** \brief convert a boolean to a string
* \ingroup jkqtptools_string * \ingroup jkqtptools_string
*/ */

View File

@ -433,7 +433,7 @@ QString JKQTPCoordinateAxis::floattolabel(double data, int past_comma) const {
case JKQTPCALTscientific: case JKQTPCALTscientific:
return addTickUnit(floattostringWithFormat(loc, data, 'e', past_comma, remove_trail0)); return addTickUnit(floattostringWithFormat(loc, data, 'e', past_comma, remove_trail0));
case JKQTPCALTexponent: case JKQTPCALTexponent:
return addTickUnit(QString(jkqtp_floattolatexstr(data, past_comma, remove_trail0, belowIsZero, pow(10, -past_comma), pow(10, past_comma+1)).c_str())); return addTickUnit(QString(jkqtp_floattolatexstr(data, past_comma, remove_trail0, belowIsZero, pow(10.0, -static_cast<double>(past_comma)-0.0001), pow(10.0, static_cast<double>(past_comma)+1)).c_str()));
case JKQTPCALTexponentCharacter: case JKQTPCALTexponentCharacter:
return addTickUnit(QString(jkqtp_floattolatexunitstr(data, past_comma, remove_trail0).c_str())); return addTickUnit(QString(jkqtp_floattolatexunitstr(data, past_comma, remove_trail0).c_str()));
case JKQTPCALTintfrac: case JKQTPCALTintfrac:
@ -446,7 +446,7 @@ QString JKQTPCoordinateAxis::floattolabel(double data, int past_comma) const {
uint64_t denom=0; uint64_t denom=0;
uint64_t intpart=0; uint64_t intpart=0;
int sign=+1; int sign=+1;
const double powfac=pow(10,past_comma); const double powfac=pow(10.0,static_cast<double>(past_comma));
const double rounded=round(data*powfac)/powfac; const double rounded=round(data*powfac)/powfac;
jkqtp_estimateFraction(rounded, sign, intpart, num, denom); jkqtp_estimateFraction(rounded, sign, intpart, num, denom);
//std::cout<<"\n"<<data<<" => "<<rounded<<", "<<sign<<"*( "<<intpart<<" + "<<num<<"/"<<denom<<" )\n"; //std::cout<<"\n"<<data<<" => "<<rounded<<", "<<sign<<"*( "<<intpart<<" + "<<num<<"/"<<denom<<" )\n";

View File

@ -102,6 +102,37 @@ private slots:
QCOMPARE_EQ(n, QColor::fromRgbF(0.52,0.52,0.52,240.0/255.0)); QCOMPARE_EQ(n, QColor::fromRgbF(0.52,0.52,0.52,240.0/255.0));
} }
inline void test_jkqtp_floattounitstr() {
QCOMPARE_EQ(jkqtp_floattounitstr(0, 1, true), "0");
QCOMPARE_EQ(jkqtp_floattounitstr(0, 1, false), "0");
QCOMPARE_EQ(jkqtp_floattounitstr(1.0, 1, true), "1");
QCOMPARE_EQ(jkqtp_floattounitstr(1.0, 1, false), "1.0");
QCOMPARE_EQ(jkqtp_floattounitstr(1.2e3, 1, false), "1.2k");
QCOMPARE_EQ(jkqtp_floattounitstr(-1.2e6, 1, false), "-1.2M");
QCOMPARE_EQ(jkqtp_floattounitstr(-1.2e30, 1, false), "-1.2Q");
QCOMPARE_EQ(jkqtp_floattounitstr(1.2e30, 1, false), "1.2Q");
QCOMPARE_EQ(jkqtp_floattounitstr(1.2e-3, 1, false), "1.2m");
QCOMPARE_EQ(jkqtp_floattounitstr(-1.2e-6, 1, false), "-1.2\xB5");
QCOMPARE_EQ(jkqtp_floattounitstr(-1.2e-30, 1, false), "-1.2q");
QCOMPARE_EQ(jkqtp_floattounitstr(1.2e-30, 1, false), "1.2q");
QCOMPARE_EQ(jkqtp_floattounitstr(1.234e-30, 1, false), "1.2q");
}
inline void test_jkqtp_floattolatexunitstr() {
QCOMPARE_EQ(jkqtp_floattolatexunitstr(0, 1, true), "0");
QCOMPARE_EQ(jkqtp_floattolatexunitstr(0, 1, false), "0");
QCOMPARE_EQ(jkqtp_floattolatexunitstr(1.0, 1, true), "1");
QCOMPARE_EQ(jkqtp_floattolatexunitstr(1.0, 1, false), "1.0");
QCOMPARE_EQ(jkqtp_floattolatexunitstr(1.2e3, 1, false), "1.2\\;\\mathrm{k}");
QCOMPARE_EQ(jkqtp_floattolatexunitstr(-1.2e6, 1, false), "-1.2\\;\\mathrm{M}");
QCOMPARE_EQ(jkqtp_floattolatexunitstr(-1.2e30, 1, false), "-1.2\\;\\mathrm{Q}");
QCOMPARE_EQ(jkqtp_floattolatexunitstr(1.2e30, 1, false), "1.2\\;\\mathrm{Q}");
QCOMPARE_EQ(jkqtp_floattolatexunitstr(1.2e-3, 1, false), "1.2\\;\\mathrm{m}");
QCOMPARE_EQ(jkqtp_floattolatexunitstr(-1.2e-6, 1, false), "-1.2\\;\\mathrm{\\mu}");
QCOMPARE_EQ(jkqtp_floattolatexunitstr(-1.2e-30, 1, false), "-1.2\\;\\mathrm{q}");
QCOMPARE_EQ(jkqtp_floattolatexunitstr(1.2e-30, 1, false), "1.2\\;\\mathrm{q}");
QCOMPARE_EQ(jkqtp_floattolatexunitstr(1.234e-30, 1, false), "1.2\\;\\mathrm{q}");
}
}; };