1
0
Fork 0
mirror of https://github.com/gwm17/implot.git synced 2024-11-26 12:18:52 -05:00

constrain values <= 0 to DBL_MIN for log axes, fix div by zero

This commit is contained in:
Evan Pezent 2021-06-08 20:31:30 -07:00
parent 0cafcd0011
commit e9469cf347
3 changed files with 39 additions and 31 deletions

View File

@ -507,34 +507,36 @@ void PullLinkedAxis(ImPlotAxis& axis) {
void UpdateTransformCache() { void UpdateTransformCache() {
ImPlotContext& gp = *GImPlot; ImPlotContext& gp = *GImPlot;
ImPlotPlot& plot = *gp.CurrentPlot;
// get pixels for transforms // get pixels for transforms
for (int i = 0; i < IMPLOT_Y_AXES; i++) { for (int i = 0; i < IMPLOT_Y_AXES; i++) {
gp.PixelRange[i] = ImRect(ImHasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_Invert) ? gp.CurrentPlot->PlotRect.Max.x : gp.CurrentPlot->PlotRect.Min.x, gp.PixelRange[i] = ImRect(plot.XAxis.IsInverted() ? plot.PlotRect.Max.x : plot.PlotRect.Min.x,
ImHasFlag(gp.CurrentPlot->YAxis[i].Flags, ImPlotAxisFlags_Invert) ? gp.CurrentPlot->PlotRect.Min.y : gp.CurrentPlot->PlotRect.Max.y, plot.YAxis[i].IsInverted() ? plot.PlotRect.Min.y : plot.PlotRect.Max.y,
ImHasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_Invert) ? gp.CurrentPlot->PlotRect.Min.x : gp.CurrentPlot->PlotRect.Max.x, plot.XAxis.IsInverted() ? plot.PlotRect.Min.x : plot.PlotRect.Max.x,
ImHasFlag(gp.CurrentPlot->YAxis[i].Flags, ImPlotAxisFlags_Invert) ? gp.CurrentPlot->PlotRect.Max.y : gp.CurrentPlot->PlotRect.Min.y); plot.YAxis[i].IsInverted() ? plot.PlotRect.Max.y : plot.PlotRect.Min.y);
gp.My[i] = (gp.PixelRange[i].Max.y - gp.PixelRange[i].Min.y) / gp.CurrentPlot->YAxis[i].Range.Size(); gp.My[i] = (gp.PixelRange[i].Max.y - gp.PixelRange[i].Min.y) / plot.YAxis[i].Range.Size();
} }
gp.LogDenX = ImLog10(gp.CurrentPlot->XAxis.Range.Max / gp.CurrentPlot->XAxis.Range.Min); gp.LogDenX = plot.XAxis.IsLog() ? ImLog10(plot.XAxis.Range.Max / plot.XAxis.Range.Min) : 0;
for (int i = 0; i < IMPLOT_Y_AXES; i++) for (int i = 0; i < IMPLOT_Y_AXES; i++)
gp.LogDenY[i] = ImLog10(gp.CurrentPlot->YAxis[i].Range.Max / gp.CurrentPlot->YAxis[i].Range.Min); gp.LogDenY[i] = plot.YAxis[i].IsLog() ? ImLog10(plot.YAxis[i].Range.Max / plot.YAxis[i].Range.Min) : 0;
gp.Mx = (gp.PixelRange[0].Max.x - gp.PixelRange[0].Min.x) / gp.CurrentPlot->XAxis.Range.Size(); gp.Mx = (gp.PixelRange[0].Max.x - gp.PixelRange[0].Min.x) / plot.XAxis.Range.Size();
} }
ImPlotPoint PixelsToPlot(float x, float y, ImPlotYAxis y_axis_in) { ImPlotPoint PixelsToPlot(float x, float y, ImPlotYAxis y_axis_in) {
ImPlotContext& gp = *GImPlot; ImPlotContext& gp = *GImPlot;
ImPlotPlot& plot = *gp.CurrentPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PixelsToPlot() needs to be called between BeginPlot() and EndPlot()!"); IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PixelsToPlot() needs to be called between BeginPlot() and EndPlot()!");
const ImPlotYAxis y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis; const ImPlotYAxis y_axis = y_axis_in >= 0 ? y_axis_in : plot.CurrentYAxis;
ImPlotPoint plt; ImPlotPoint plt;
plt.x = (x - gp.PixelRange[y_axis].Min.x) / gp.Mx + gp.CurrentPlot->XAxis.Range.Min; plt.x = (x - gp.PixelRange[y_axis].Min.x) / gp.Mx + plot.XAxis.Range.Min;
plt.y = (y - gp.PixelRange[y_axis].Min.y) / gp.My[y_axis] + gp.CurrentPlot->YAxis[y_axis].Range.Min; plt.y = (y - gp.PixelRange[y_axis].Min.y) / gp.My[y_axis] + plot.YAxis[y_axis].Range.Min;
if (ImHasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_LogScale)) { if (ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale)) {
double t = (plt.x - gp.CurrentPlot->XAxis.Range.Min) / gp.CurrentPlot->XAxis.Range.Size(); double t = (plt.x - plot.XAxis.Range.Min) / plot.XAxis.Range.Size();
plt.x = ImPow(10, t * gp.LogDenX) * gp.CurrentPlot->XAxis.Range.Min; plt.x = ImPow(10, t * gp.LogDenX) * plot.XAxis.Range.Min;
} }
if (ImHasFlag(gp.CurrentPlot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale)) { if (ImHasFlag(plot.YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale)) {
double t = (plt.y - gp.CurrentPlot->YAxis[y_axis].Range.Min) / gp.CurrentPlot->YAxis[y_axis].Range.Size(); double t = (plt.y - plot.YAxis[y_axis].Range.Min) / plot.YAxis[y_axis].Range.Size();
plt.y = ImPow(10, t * gp.LogDenY[y_axis]) * gp.CurrentPlot->YAxis[y_axis].Range.Min; plt.y = ImPow(10, t * gp.LogDenY[y_axis]) * plot.YAxis[y_axis].Range.Min;
} }
return plt; return plt;
} }
@ -543,26 +545,26 @@ ImPlotPoint PixelsToPlot(const ImVec2& pix, ImPlotYAxis y_axis) {
return PixelsToPlot(pix.x, pix.y, y_axis); return PixelsToPlot(pix.x, pix.y, y_axis);
} }
// This function is convenient but should not be used to process a high volume of points. Use the Transformer structs below instead.
ImVec2 PlotToPixels(double x, double y, ImPlotYAxis y_axis_in) { ImVec2 PlotToPixels(double x, double y, ImPlotYAxis y_axis_in) {
ImPlotContext& gp = *GImPlot; ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotToPixels() needs to be called between BeginPlot() and EndPlot()!"); IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotToPixels() needs to be called between BeginPlot() and EndPlot()!");
const ImPlotYAxis y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis; const ImPlotYAxis y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis;
ImVec2 pix; ImVec2 pix;
if (ImHasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_LogScale)) { if (ImHasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_LogScale)) {
x = x <= 0.0 ? IMPLOT_LOG_ZERO : x;
double t = ImLog10(x / gp.CurrentPlot->XAxis.Range.Min) / gp.LogDenX; double t = ImLog10(x / gp.CurrentPlot->XAxis.Range.Min) / gp.LogDenX;
x = ImLerp(gp.CurrentPlot->XAxis.Range.Min, gp.CurrentPlot->XAxis.Range.Max, (float)t); x = ImLerp(gp.CurrentPlot->XAxis.Range.Min, gp.CurrentPlot->XAxis.Range.Max, (float)t);
} }
if (ImHasFlag(gp.CurrentPlot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale)) { if (ImHasFlag(gp.CurrentPlot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale)) {
y = y <= 0.0 ? IMPLOT_LOG_ZERO : y;
double t = ImLog10(y / gp.CurrentPlot->YAxis[y_axis].Range.Min) / gp.LogDenY[y_axis]; double t = ImLog10(y / gp.CurrentPlot->YAxis[y_axis].Range.Min) / gp.LogDenY[y_axis];
y = ImLerp(gp.CurrentPlot->YAxis[y_axis].Range.Min, gp.CurrentPlot->YAxis[y_axis].Range.Max, (float)t); y = ImLerp(gp.CurrentPlot->YAxis[y_axis].Range.Min, gp.CurrentPlot->YAxis[y_axis].Range.Max, (float)t);
} }
pix.x = (float)(gp.PixelRange[y_axis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min)); pix.x = (float)(gp.PixelRange[y_axis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min));
pix.y = (float)(gp.PixelRange[y_axis].Min.y + gp.My[y_axis] * (y - gp.CurrentPlot->YAxis[y_axis].Range.Min)); pix.y = (float)(gp.PixelRange[y_axis].Min.y + gp.My[y_axis] * (y - gp.CurrentPlot->YAxis[y_axis].Range.Min));
return pix; return pix;
} }
// This function is convenient but should not be used to process a high volume of points. Use the Transformer structs below instead.
ImVec2 PlotToPixels(const ImPlotPoint& plt, ImPlotYAxis y_axis) { ImVec2 PlotToPixels(const ImPlotPoint& plt, ImPlotYAxis y_axis) {
return PlotToPixels(plt.x, plt.y, y_axis); return PlotToPixels(plt.x, plt.y, y_axis);
} }

View File

@ -59,6 +59,8 @@
#define IMPLOT_MAX_TIME 32503680000 #define IMPLOT_MAX_TIME 32503680000
// Default label format for axis labels // Default label format for axis labels
#define IMPLOT_LABEL_FMT "%g" #define IMPLOT_LABEL_FMT "%g"
// Plot values less than or equal to 0 will be replaced with this on log scale axes
#define IMPLOT_LOG_ZERO DBL_MIN
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// [SECTION] Macros // [SECTION] Macros
@ -666,11 +668,11 @@ struct ImPlotAxis
void Constrain() { void Constrain() {
Range.Min = ImConstrainNan(ImConstrainInf(Range.Min)); Range.Min = ImConstrainNan(ImConstrainInf(Range.Min));
Range.Max = ImConstrainNan(ImConstrainInf(Range.Max)); Range.Max = ImConstrainNan(ImConstrainInf(Range.Max));
if (ImHasFlag(Flags, ImPlotAxisFlags_LogScale)) { if (IsLog()) {
Range.Min = ImConstrainLog(Range.Min); Range.Min = ImConstrainLog(Range.Min);
Range.Max = ImConstrainLog(Range.Max); Range.Max = ImConstrainLog(Range.Max);
} }
if (ImHasFlag(Flags, ImPlotAxisFlags_Time)) { if (IsTime()) {
Range.Min = ImConstrainTime(Range.Min); Range.Min = ImConstrainTime(Range.Min);
Range.Max = ImConstrainTime(Range.Max); Range.Max = ImConstrainTime(Range.Max);
} }

View File

@ -435,8 +435,9 @@ struct TransformerLogLin {
TransformerLogLin() : YAxis(GetCurrentYAxis()) {} TransformerLogLin() : YAxis(GetCurrentYAxis()) {}
inline ImVec2 operator()(const ImPlotPoint& plt) const { inline ImVec2 operator()(const ImPlotPoint& plt) const {
ImPlotContext& gp = *GImPlot; ImPlotContext& gp = *GImPlot;
double t = ImLog10(plt.x / gp.CurrentPlot->XAxis.Range.Min) / gp.LogDenX; double x = plt.x <= 0.0 ? IMPLOT_LOG_ZERO : plt.x;
double x = ImLerp(gp.CurrentPlot->XAxis.Range.Min, gp.CurrentPlot->XAxis.Range.Max, (float)t); double t = ImLog10(x / gp.CurrentPlot->XAxis.Range.Min) / gp.LogDenX;
x = ImLerp(gp.CurrentPlot->XAxis.Range.Min, gp.CurrentPlot->XAxis.Range.Max, (float)t);
return ImVec2( (float)(gp.PixelRange[YAxis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min)), return ImVec2( (float)(gp.PixelRange[YAxis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min)),
(float)(gp.PixelRange[YAxis].Min.y + gp.My[YAxis] * (plt.y - gp.CurrentPlot->YAxis[YAxis].Range.Min)) ); (float)(gp.PixelRange[YAxis].Min.y + gp.My[YAxis] * (plt.y - gp.CurrentPlot->YAxis[YAxis].Range.Min)) );
} }
@ -448,8 +449,9 @@ struct TransformerLinLog {
TransformerLinLog() : YAxis(GetCurrentYAxis()) {} TransformerLinLog() : YAxis(GetCurrentYAxis()) {}
inline ImVec2 operator()(const ImPlotPoint& plt) const { inline ImVec2 operator()(const ImPlotPoint& plt) const {
ImPlotContext& gp = *GImPlot; ImPlotContext& gp = *GImPlot;
double t = ImLog10(plt.y / gp.CurrentPlot->YAxis[YAxis].Range.Min) / gp.LogDenY[YAxis]; double y = plt.y <= 0.0 ? IMPLOT_LOG_ZERO : plt.y;
double y = ImLerp(gp.CurrentPlot->YAxis[YAxis].Range.Min, gp.CurrentPlot->YAxis[YAxis].Range.Max, (float)t); double t = ImLog10(y / gp.CurrentPlot->YAxis[YAxis].Range.Min) / gp.LogDenY[YAxis];
y = ImLerp(gp.CurrentPlot->YAxis[YAxis].Range.Min, gp.CurrentPlot->YAxis[YAxis].Range.Max, (float)t);
return ImVec2( (float)(gp.PixelRange[YAxis].Min.x + gp.Mx * (plt.x - gp.CurrentPlot->XAxis.Range.Min)), return ImVec2( (float)(gp.PixelRange[YAxis].Min.x + gp.Mx * (plt.x - gp.CurrentPlot->XAxis.Range.Min)),
(float)(gp.PixelRange[YAxis].Min.y + gp.My[YAxis] * (y - gp.CurrentPlot->YAxis[YAxis].Range.Min)) ); (float)(gp.PixelRange[YAxis].Min.y + gp.My[YAxis] * (y - gp.CurrentPlot->YAxis[YAxis].Range.Min)) );
} }
@ -461,10 +463,12 @@ struct TransformerLogLog {
TransformerLogLog() : YAxis(GetCurrentYAxis()) {} TransformerLogLog() : YAxis(GetCurrentYAxis()) {}
inline ImVec2 operator()(const ImPlotPoint& plt) const { inline ImVec2 operator()(const ImPlotPoint& plt) const {
ImPlotContext& gp = *GImPlot; ImPlotContext& gp = *GImPlot;
double t = ImLog10(plt.x / gp.CurrentPlot->XAxis.Range.Min) / gp.LogDenX; double x = plt.x <= 0.0 ? IMPLOT_LOG_ZERO : plt.x;
double x = ImLerp(gp.CurrentPlot->XAxis.Range.Min, gp.CurrentPlot->XAxis.Range.Max, (float)t); double y = plt.y <= 0.0 ? IMPLOT_LOG_ZERO : plt.y;
t = ImLog10(plt.y / gp.CurrentPlot->YAxis[YAxis].Range.Min) / gp.LogDenY[YAxis]; double t = ImLog10(x / gp.CurrentPlot->XAxis.Range.Min) / gp.LogDenX;
double y = ImLerp(gp.CurrentPlot->YAxis[YAxis].Range.Min, gp.CurrentPlot->YAxis[YAxis].Range.Max, (float)t); x = ImLerp(gp.CurrentPlot->XAxis.Range.Min, gp.CurrentPlot->XAxis.Range.Max, (float)t);
t = ImLog10(y / gp.CurrentPlot->YAxis[YAxis].Range.Min) / gp.LogDenY[YAxis];
y = ImLerp(gp.CurrentPlot->YAxis[YAxis].Range.Min, gp.CurrentPlot->YAxis[YAxis].Range.Max, (float)t);
return ImVec2( (float)(gp.PixelRange[YAxis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min)), return ImVec2( (float)(gp.PixelRange[YAxis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min)),
(float)(gp.PixelRange[YAxis].Min.y + gp.My[YAxis] * (y - gp.CurrentPlot->YAxis[YAxis].Range.Min)) ); (float)(gp.PixelRange[YAxis].Min.y + gp.My[YAxis] * (y - gp.CurrentPlot->YAxis[YAxis].Range.Min)) );
} }