1
0
Fork 0
mirror of https://github.com/gwm17/implot.git synced 2024-11-13 22:48:50 -05:00

add ImPlotAxisFlags_RangeFit and ImPlotAxisFlags_Foreground (#200)

This commit is contained in:
Evan Pezent 2021-03-28 12:59:25 -07:00 committed by GitHub
parent 707a0bee36
commit 41a0e2c9fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 132 additions and 80 deletions

View File

@ -487,32 +487,6 @@ void BustPlotCache() {
GImPlot->Plots.Clear();
}
void FitPoint(const ImPlotPoint& p) {
FitPointX(p.x);
FitPointY(p.y);
}
void FitPointX(double x) {
ImPlotContext& gp = *GImPlot;
ImPlotRange& ex_x = gp.ExtentsX;
const bool log_x = ImHasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_LogScale);
if (!ImNanOrInf(x) && !(log_x && x <= 0)) {
ex_x.Min = x < ex_x.Min ? x : ex_x.Min;
ex_x.Max = x > ex_x.Max ? x : ex_x.Max;
}
}
void FitPointY(double y) {
ImPlotContext& gp = *GImPlot;
const ImPlotYAxis y_axis = gp.CurrentPlot->CurrentYAxis;
ImPlotRange& ex_y = gp.ExtentsY[y_axis];
const bool log_y = ImHasFlag(gp.CurrentPlot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale);
if (!ImNanOrInf(y) && !(log_y && y <= 0)) {
ex_y.Min = y < ex_y.Min ? y : ex_y.Min;
ex_y.Max = y > ex_y.Max ? y : ex_y.Max;
}
}
void PushLinkedAxis(ImPlotAxis& axis) {
if (axis.LinkedMin) { *axis.LinkedMin = axis.Range.Min; }
if (axis.LinkedMax) { *axis.LinkedMax = axis.Range.Max; }
@ -1301,6 +1275,36 @@ void UpdateAxisColors(int axis_flag, ImPlotAxis* axis) {
// RENDERING
//-----------------------------------------------------------------------------
static inline void RenderGridLinesX(ImDrawList& DrawList, const ImPlotTickCollection& ticks, const ImRect& rect, ImU32 col_maj, ImU32 col_min, float size_maj, float size_min) {
const float density = ticks.Size / rect.GetWidth();
ImVec4 col_min4 = ImGui::ColorConvertU32ToFloat4(col_min);
col_min4.w *= ImClamp(ImRemap(density, 0.1f, 0.2f, 1.0f, 0.0f), 0.0f, 1.0f);
col_min = ImGui::ColorConvertFloat4ToU32(col_min4);
for (int t = 0; t < ticks.Size; t++) {
const ImPlotTick& xt = ticks.Ticks[t];
if (xt.Level == 0) {
if (xt.Major)
DrawList.AddLine(ImVec2(xt.PixelPos, rect.Min.y), ImVec2(xt.PixelPos, rect.Max.y), col_maj, size_maj);
else if (density < 0.2f)
DrawList.AddLine(ImVec2(xt.PixelPos, rect.Min.y), ImVec2(xt.PixelPos, rect.Max.y), col_min, size_min);
}
}
}
static inline void RenderGridLinesY(ImDrawList& DrawList, const ImPlotTickCollection& ticks, const ImRect& rect, ImU32 col_maj, ImU32 col_min, float size_maj, float size_min) {
const float density = ticks.Size / rect.GetHeight();
ImVec4 col_min4 = ImGui::ColorConvertU32ToFloat4(col_min);
col_min4.w *= ImClamp(ImRemap(density, 0.1f, 0.2f, 1.0f, 0.0f), 0.0f, 1.0f);
col_min = ImGui::ColorConvertFloat4ToU32(col_min4);
for (int t = 0; t < ticks.Size; t++) {
const ImPlotTick& yt = ticks.Ticks[t];
if (yt.Major)
DrawList.AddLine(ImVec2(rect.Min.x, yt.PixelPos), ImVec2(rect.Max.x, yt.PixelPos), col_maj, size_maj);
else if (density < 0.2f)
DrawList.AddLine(ImVec2(rect.Min.x, yt.PixelPos), ImVec2(rect.Max.x, yt.PixelPos), col_min, size_min);
}
}
static inline void RenderSelectionRect(ImDrawList& DrawList, const ImVec2& p_min, const ImVec2& p_max, const ImVec4& col) {
const ImU32 col_bg = ImGui::GetColorU32(col * ImVec4(1,1,1,0.25f));
const ImU32 col_bd = ImGui::GetColorU32(col);
@ -1909,9 +1913,6 @@ bool BeginPlot(const char* title, const char* x_label, const char* y1_label, con
// grid bg
DrawList.AddRectFilled(plot.PlotRect.Min, plot.PlotRect.Max, GetStyleColorU32(ImPlotCol_PlotBg));
// render axes
PushPlotClipRect();
// transform ticks (TODO: Move this into ImPlotTickCollection)
if (gp.RenderX) {
for (int t = 0; t < gp.XTicks.Size; t++) {
@ -1928,39 +1929,14 @@ bool BeginPlot(const char* title, const char* x_label, const char* y1_label, con
}
}
// render grid
if (!ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_NoGridLines)) {
float density = gp.XTicks.Size / plot.PlotRect.GetWidth();
ImVec4 col_min = ImGui::ColorConvertU32ToFloat4(plot.XAxis.ColorMin);
col_min.w *= ImClamp(ImRemap(density, 0.1f, 0.2f, 1.0f, 0.0f), 0.0f, 1.0f);
ImU32 col_min32 = ImGui::ColorConvertFloat4ToU32(col_min);
for (int t = 0; t < gp.XTicks.Size; t++) {
ImPlotTick& xt = gp.XTicks.Ticks[t];
if (xt.Level == 0) {
if (xt.Major)
DrawList.AddLine(ImVec2(xt.PixelPos, plot.PlotRect.Min.y), ImVec2(xt.PixelPos, plot.PlotRect.Max.y), plot.XAxis.ColorMaj, gp.Style.MajorGridSize.x);
else if (density < 0.2f)
DrawList.AddLine(ImVec2(xt.PixelPos, plot.PlotRect.Min.y), ImVec2(xt.PixelPos, plot.PlotRect.Max.y), col_min32, gp.Style.MinorGridSize.x);
}
}
}
// render grid (background)
PushPlotClipRect(gp.Style.PlotBorderSize == 0 ? 1.0f : 0.0f);
if (!ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_NoGridLines) && !ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_Foreground))
RenderGridLinesX(DrawList, gp.XTicks, plot.PlotRect, plot.XAxis.ColorMaj, plot.XAxis.ColorMin, gp.Style.MajorGridSize.x, gp.Style.MinorGridSize.x);
for (int i = 0; i < IMPLOT_Y_AXES; i++) {
if (plot.YAxis[i].Present && !ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_NoGridLines)) {
float density = gp.YTicks[i].Size / plot.PlotRect.GetHeight();
ImVec4 col_min = ImGui::ColorConvertU32ToFloat4(plot.YAxis[i].ColorMin);
col_min.w *= ImClamp(ImRemap(density, 0.1f, 0.2f, 1.0f, 0.0f), 0.0f, 1.0f);
ImU32 col_min32 = ImGui::ColorConvertFloat4ToU32(col_min);
for (int t = 0; t < gp.YTicks[i].Size; t++) {
ImPlotTick& yt = gp.YTicks[i].Ticks[t];
if (yt.Major)
DrawList.AddLine(ImVec2(plot.PlotRect.Min.x, yt.PixelPos), ImVec2(plot.PlotRect.Max.x, yt.PixelPos), plot.YAxis[i].ColorMaj, gp.Style.MajorGridSize.y);
else if (density < 0.2f)
DrawList.AddLine(ImVec2(plot.PlotRect.Min.x, yt.PixelPos), ImVec2(plot.PlotRect.Max.x, yt.PixelPos), col_min32, gp.Style.MinorGridSize.y);
if (plot.YAxis[i].Present && !ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_NoGridLines) && !ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_Foreground))
RenderGridLinesY(DrawList, gp.YTicks[i], plot.PlotRect, plot.YAxis[i].ColorMaj, plot.YAxis[i].ColorMin, gp.Style.MajorGridSize.y, gp.Style.MinorGridSize.y);
}
}
}
PopPlotClipRect();
// render title
@ -2262,7 +2238,17 @@ void EndPlot() {
// FINAL RENDER -----------------------------------------------------------
// render ticks
// render grid (foreground)
PushPlotClipRect(gp.Style.PlotBorderSize == 0 ? 1.0f : 0.0f);
if (!ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_NoGridLines) && ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_Foreground))
RenderGridLinesX(DrawList, gp.XTicks, plot.PlotRect, plot.XAxis.ColorMaj, plot.XAxis.ColorMaj, gp.Style.MajorGridSize.x, gp.Style.MinorGridSize.x);
for (int i = 0; i < IMPLOT_Y_AXES; i++) {
if (plot.YAxis[i].Present && !ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_NoGridLines) && ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_Foreground))
RenderGridLinesY(DrawList, gp.YTicks[i], plot.PlotRect, plot.YAxis[i].ColorMaj, plot.YAxis[i].ColorMin, gp.Style.MajorGridSize.y, gp.Style.MinorGridSize.y);
}
PopPlotClipRect();
// render x-ticks
PushPlotClipRect();
if (!ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_NoTickMarks)) {
for (int t = 0; t < gp.XTicks.Size; t++) {
@ -2276,12 +2262,12 @@ void EndPlot() {
}
PopPlotClipRect();
// render y-ticks
ImGui::PushClipRect(plot.PlotRect.Min, ImVec2(plot.FrameRect.Max.x, plot.PlotRect.Max.y), true);
int axis_count = 0;
for (int i = 0; i < IMPLOT_Y_AXES; i++) {
if (!plot.YAxis[i].Present) { continue; }
axis_count++;
float x_start = gp.YAxisReference[i];
if (!ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_NoTickMarks)) {
float direction = (i == 0) ? 1.0f : -1.0f;
@ -2295,7 +2281,6 @@ void EndPlot() {
(!no_major && yt->Major) ? gp.Style.MajorTickSize.y : gp.Style.MinorTickSize.y);
}
}
if (axis_count >= 3) {
// Draw a bar next to the ticks to act as a visual separator.
DrawList.AddLine(ImVec2(x_start, plot.PlotRect.Min.y), ImVec2(x_start, plot.PlotRect.Max.y), GetStyleColorU32(ImPlotCol_YAxisGrid3), 1);
@ -2674,10 +2659,12 @@ ImDrawList* GetPlotDrawList() {
return ImGui::GetWindowDrawList();
}
void PushPlotClipRect() {
void PushPlotClipRect(float expand) {
ImPlotContext& gp = *GImPlot;
ImRect rect = gp.CurrentPlot->PlotRect;
rect.Expand(expand);
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PushPlotClipRect() needs to be called between BeginPlot() and EndPlot()!");
ImGui::PushClipRect(gp.CurrentPlot->PlotRect.Min, gp.CurrentPlot->PlotRect.Max, true);
ImGui::PushClipRect(rect.Min, rect.Max, true);
}
void PopPlotClipRect() {

View File

@ -89,13 +89,15 @@ enum ImPlotAxisFlags_ {
ImPlotAxisFlags_NoGridLines = 1 << 1, // no grid lines will be displayed
ImPlotAxisFlags_NoTickMarks = 1 << 2, // no tick marks will be displayed
ImPlotAxisFlags_NoTickLabels = 1 << 3, // no text labels will be displayed
ImPlotAxisFlags_LogScale = 1 << 4, // a logartithmic (base 10) axis scale will be used (mutually exclusive with ImPlotAxisFlags_Time)
ImPlotAxisFlags_Time = 1 << 5, // axis will display date/time formatted labels (mutually exclusive with ImPlotAxisFlags_LogScale)
ImPlotAxisFlags_Invert = 1 << 6, // the axis will be inverted
ImPlotAxisFlags_NoInitialFit = 1 << 7, // axis will not be initially fit to data extents on the first rendered frame (also the case if SetNextPlotLimits explicitly called)
ImPlotAxisFlags_AutoFit = 1 << 8, // axis will be auto-fitting to data extents
ImPlotAxisFlags_LockMin = 1 << 9, // the axis minimum value will be locked when panning/zooming
ImPlotAxisFlags_LockMax = 1 << 10, // the axis maximum value will be locked when panning/zooming
ImPlotAxisFlags_Foreground = 1 << 4, // grid lines will be displayed in the foreground (i.e. on top of data) in stead of the background
ImPlotAxisFlags_LogScale = 1 << 5, // a logartithmic (base 10) axis scale will be used (mutually exclusive with ImPlotAxisFlags_Time)
ImPlotAxisFlags_Time = 1 << 6, // axis will display date/time formatted labels (mutually exclusive with ImPlotAxisFlags_LogScale)
ImPlotAxisFlags_Invert = 1 << 7, // the axis will be inverted
ImPlotAxisFlags_NoInitialFit = 1 << 8, // axis will not be initially fit to data extents on the first rendered frame (also the case if SetNextPlotLimits explicitly called)
ImPlotAxisFlags_AutoFit = 1 << 9, // axis will be auto-fitting to data extents
ImPlotAxisFlags_RangeFit = 1 << 10, // axis will only fit points if the point is in the visible range of the **orthoganol** axis
ImPlotAxisFlags_LockMin = 1 << 11, // the axis minimum value will be locked when panning/zooming
ImPlotAxisFlags_LockMax = 1 << 12, // the axis maximum value will be locked when panning/zooming
ImPlotAxisFlags_Lock = ImPlotAxisFlags_LockMin | ImPlotAxisFlags_LockMax,
ImPlotAxisFlags_NoDecorations = ImPlotAxisFlags_NoLabel | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks | ImPlotAxisFlags_NoTickLabels
};
@ -808,8 +810,8 @@ IMPLOT_API void ColormapIcon(ImPlotColormap cmap);
// Get the plot draw list for custom rendering to the current plot area. Call between Begin/EndPlot.
IMPLOT_API ImDrawList* GetPlotDrawList();
// Push clip rect for rendering to current plot area. Call between Begin/EndPlot.
IMPLOT_API void PushPlotClipRect();
// Push clip rect for rendering to current plot area. The rect can be expanded or contracted by #expand pixels. Call between Begin/EndPlot.
IMPLOT_API void PushPlotClipRect(float expand=0);
// Pop plot clip rect. Call between Begin/EndPlot.
IMPLOT_API void PopPlotClipRect();

View File

@ -654,8 +654,9 @@ void ShowDemoWindow(bool* p_open) {
static NormalDistribution<500000> dist1(1, 2);
static NormalDistribution<500000> dist2(1, 1);
double max_count = 0;
ImPlot::PushColormap("Twilight");
if (ImPlot::BeginPlot("##Hist2D",0,0,ImVec2(ImGui::GetContentRegionAvail().x-100-ImGui::GetStyle().ItemSpacing.x,0),0,ImPlotAxisFlags_AutoFit,ImPlotAxisFlags_AutoFit)) {
ImPlotAxisFlags flags = ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_Foreground;
ImPlot::PushColormap("Hot");
if (ImPlot::BeginPlot("##Hist2D",0,0,ImVec2(ImGui::GetContentRegionAvail().x-100-ImGui::GetStyle().ItemSpacing.x,0),0,flags,flags)) {
max_count = ImPlot::PlotHistogram2D("Hist2D",dist1.Data,dist2.Data,count,xybins[0],xybins[1],density2,ImPlotLimits(-6,6,-6,6));
ImPlot::EndPlot();
}
@ -952,6 +953,35 @@ void ShowDemoWindow(bool* p_open) {
}
}
//-------------------------------------------------------------------------
if (ImGui::CollapsingHeader("Auto-Fitting Data")) {
ImGui::BulletText("The Y-axis has been configured to auto-fit to only the data visible in X-axis range.");
ImGui::BulletText("Zoom and pan the X-axis. Disable Stems to see a difference in fit.");
ImGui::BulletText("If ImPlotAxisFlags_RangeFit is disabled, the axis will fit ALL data.");
static ImPlotAxisFlags xflags = ImPlotAxisFlags_None;
static ImPlotAxisFlags yflags = ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_RangeFit;
ImGui::TextUnformatted("X: "); ImGui::SameLine();
ImGui::CheckboxFlags("ImPlotAxisFlags_AutoFit##X", (unsigned int*)&xflags, ImPlotAxisFlags_AutoFit); ImGui::SameLine();
ImGui::CheckboxFlags("ImPlotAxisFlags_RangeFit##X", (unsigned int*)&xflags, ImPlotAxisFlags_RangeFit);
ImGui::TextUnformatted("Y: "); ImGui::SameLine();
ImGui::CheckboxFlags("ImPlotAxisFlags_AutoFit##Y", (unsigned int*)&yflags, ImPlotAxisFlags_AutoFit); ImGui::SameLine();
ImGui::CheckboxFlags("ImPlotAxisFlags_RangeFit##Y", (unsigned int*)&yflags, ImPlotAxisFlags_RangeFit);
static double data[101];
srand(0);
for (int i = 0; i < 101; ++i)
data[i] = 1 + sin(i/10.0f);
if (ImPlot::BeginPlot("##DataFitting","X","Y",ImVec2(-1,0),0,xflags,yflags)) {
ImPlot::PlotLine("Line",data,101);
ImPlot::PlotStems("Stems",data,101);
ImPlot::EndPlot();
};
}
//-------------------------------------------------------------------------
if (ImGui::CollapsingHeader("Querying")) {
static ImVector<ImPlotPoint> data;
static ImPlotLimits range, query, select;

View File

@ -987,12 +987,37 @@ static inline ImPlotScale GetCurrentScale() { return GImPlot->Scales[GetCurrentY
// Returns true if the user has requested data to be fit.
static inline bool FitThisFrame() { return GImPlot->FitThisFrame; }
// Extends the current plot's axes so that it encompasses point p
IMPLOT_API void FitPoint(const ImPlotPoint& p);
// Extend the the extents of an axis on current plot so that it encompes v
static inline void FitPointAxis(ImPlotAxis& axis, ImPlotRange& ext, double v) {
if (!ImNanOrInf(v) && !(ImHasFlag(axis.Flags, ImPlotAxisFlags_LogScale) && v <= 0)) {
ext.Min = v < ext.Min ? v : ext.Min;
ext.Max = v > ext.Max ? v : ext.Max;
}
}
// Extend the the extents of an axis on current plot so that it encompes v
static inline void FitPointMultiAxis(ImPlotAxis& axis, ImPlotAxis& alt, ImPlotRange& ext, double v, double v_alt) {
if (ImHasFlag(axis.Flags, ImPlotAxisFlags_RangeFit) && !alt.Range.Contains(v_alt))
return;
if (!ImNanOrInf(v) && !(ImHasFlag(axis.Flags, ImPlotAxisFlags_LogScale) && v <= 0)) {
ext.Min = v < ext.Min ? v : ext.Min;
ext.Max = v > ext.Max ? v : ext.Max;
}
}
// Extends the current plot's axes so that it encompasses a vertical line at x
IMPLOT_API void FitPointX(double x);
static inline void FitPointX(double x) {
FitPointAxis(GImPlot->CurrentPlot->XAxis, GImPlot->ExtentsX, x);
}
// Extends the current plot's axes so that it encompasses a horizontal line at y
IMPLOT_API void FitPointY(double y);
static inline void FitPointY(double y) {
const ImPlotYAxis y_axis = GImPlot->CurrentPlot->CurrentYAxis;
FitPointAxis(GImPlot->CurrentPlot->YAxis[y_axis], GImPlot->ExtentsY[y_axis], y);
}
// Extends the current plot's axes so that it encompasses point p
static inline void FitPoint(const ImPlotPoint& p) {
const ImPlotYAxis y_axis = GImPlot->CurrentPlot->CurrentYAxis;
FitPointMultiAxis(GImPlot->CurrentPlot->XAxis, GImPlot->CurrentPlot->YAxis[y_axis], GImPlot->ExtentsX, p.x, p.y);
FitPointMultiAxis(GImPlot->CurrentPlot->YAxis[y_axis], GImPlot->CurrentPlot->XAxis, GImPlot->ExtentsY[y_axis], p.y, p.x);
}
// Returns true if two ranges overlap
static inline bool RangesOverlap(const ImPlotRange& r1, const ImPlotRange& r2)

View File

@ -918,6 +918,8 @@ inline void PlotLineEx(const char* label_id, const Getter& getter) {
}
// render markers
if (s.Marker != ImPlotMarker_None) {
PopPlotClipRect();
PushPlotClipRect(s.MarkerSize);
const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerOutline]);
const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerFill]);
switch (GetCurrentScale()) {
@ -989,6 +991,8 @@ inline void PlotScatterEx(const char* label_id, const Getter& getter) {
// render markers
ImPlotMarker marker = s.Marker == ImPlotMarker_None ? ImPlotMarker_Circle : s.Marker;
if (marker != ImPlotMarker_None) {
PopPlotClipRect();
PushPlotClipRect(s.MarkerSize);
const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerOutline]);
const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerFill]);
switch (GetCurrentScale()) {
@ -1068,6 +1072,8 @@ inline void PlotStairsEx(const char* label_id, const Getter& getter) {
}
// render markers
if (s.Marker != ImPlotMarker_None) {
PopPlotClipRect();
PushPlotClipRect(s.MarkerSize);
const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerOutline]);
const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerFill]);
switch (GetCurrentScale()) {
@ -1550,6 +1556,8 @@ inline void PlotStemsEx(const char* label_id, const GetterM& get_mark, const Get
// render markers
ImPlotMarker marker = s.Marker == ImPlotMarker_None ? ImPlotMarker_Circle : s.Marker;
if (marker != ImPlotMarker_None) {
PopPlotClipRect();
PushPlotClipRect(s.MarkerSize);
const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerOutline]);
const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerFill]);
switch (GetCurrentScale()) {