1
0
Fork 0
mirror of https://github.com/gwm17/implot.git synced 2024-10-09 23:57:26 -04:00

finish custom ticks

This commit is contained in:
Evan Pezent 2020-06-03 14:37:01 -05:00
parent b497e7f72d
commit f59913b8e0
3 changed files with 206 additions and 83 deletions

View File

@ -150,6 +150,15 @@ struct OffsetCalculator {
int Offsets[Count];
};
template <typename T>
void FillRange(ImVector<T>& buffer, int n, T vmin, T vmax) {
buffer.resize(n);
T step = (vmax - vmin) / (n - 1);
for (int i = 0; i < n; ++i) {
buffer[i] = vmin + i * step;
}
}
/// Returns true if a flag is set
template <typename TSet, typename TFlag>
inline bool HasFlag(TSet set, TFlag flag) {
@ -234,6 +243,7 @@ struct ImPlotTick {
PlotPos = value;
Major = major;
RenderLabel = render_label;
Labeled = false;
}
double PlotPos;
float PixelPos;
@ -241,6 +251,7 @@ struct ImPlotTick {
int TextOffset;
bool Major;
bool RenderLabel;
bool Labeled;
};
struct ImPlotItem {
@ -307,10 +318,10 @@ struct ImPlotState {
struct ImPlotNextPlotData {
ImPlotNextPlotData() {
HasXRange = false;
HasUserXTickLabels = false;
ShowDefaultTicksX = true;
for (int i = 0; i < MAX_Y_AXES; ++i) {
HasYRange[i] = false;
HasUserYTickLabels[i] = false;
ShowDefaultTicksY[i] = true;
}
}
ImGuiCond XRangeCond;
@ -319,18 +330,53 @@ struct ImPlotNextPlotData {
bool HasYRange[MAX_Y_AXES];
ImPlotRange X;
ImPlotRange Y[MAX_Y_AXES];
bool HasUserXTickLabels;
bool HasUserYTickLabels[MAX_Y_AXES];
bool ShowDefaultTicksX;
bool ShowDefaultTicksY[MAX_Y_AXES];
};
/// Holds Plot state information that must persist only between calls to BeginPlot()/EndPlot()
struct ImPlotContext {
ImPlotContext() : RenderX(), RenderY() {
CurrentPlot = NULL;
FitThisFrame = FitX = false;
ChildWindowMade = false;
Reset();
SetColormap(ImPlotColormap_Default);
}
void Reset() {
// end child window if it was made
if (ChildWindowMade)
ImGui::EndChild();
ChildWindowMade = false;
// reset the next plot data
NextPlotData = ImPlotNextPlotData();
// reset items count
VisibleItemCount = 0;
// reset legend items
LegendIndices.shrink(0);
LegendLabels.Buf.shrink(0);
// reset ticks/labels
XTicks.shrink(0);
XTickLabels.Buf.shrink(0);
for (int i = 0; i < 3; ++i) {
YTicks[i].shrink(0);
YTickLabels[i].Buf.shrink(0);
}
// reset extents
FitX = false;
ExtentsX.Min = HUGE_VAL;
ExtentsX.Max = -HUGE_VAL;
for (int i = 0; i < MAX_Y_AXES; i++) {
ExtentsY[i].Min = HUGE_VAL;
ExtentsY[i].Max = -HUGE_VAL;
FitY[i] = false;
}
// reset digital plot items count
DigitalPlotItemCnt = 0;
DigitalPlotOffset = 0;
// nullify plot
CurrentPlot = NULL;
}
/// ALl Plots
ImPool<ImPlotState> Plots;
/// Current Plot
@ -370,12 +416,15 @@ struct ImPlotContext {
ImPlotRange ExtentsY[MAX_Y_AXES];
int VisibleItemCount;
bool FitThisFrame; bool FitX;
bool FitY[MAX_Y_AXES] = {};
bool FitY[MAX_Y_AXES];
// Hover states
bool Hov_Frame;
bool Hov_Grid;
// Render flags
bool RenderX, RenderY[MAX_Y_AXES];
// Lock info
bool LockPlot;
bool ChildWindowMade;
// Mouse pos
ImPlotPoint LastMousePos[MAX_Y_AXES];
// Style
@ -394,7 +443,7 @@ struct ImPlotContext {
static ImPlotContext gp;
//-----------------------------------------------------------------------------
// Utils
// Context Utils
//-----------------------------------------------------------------------------
/// Returns the next unused default plot color
@ -440,7 +489,7 @@ inline void UpdateTransformCache() {
}
inline ImPlotPoint PixelsToPlot(float x, float y, int y_axis_in = -1) {
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 int y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis;
ImPlotPoint plt;
plt.x = (x - gp.PixelRange[y_axis].Min.x) / gp.Mx + gp.CurrentPlot->XAxis.Range.Min;
@ -462,7 +511,7 @@ ImPlotPoint PixelsToPlot(const ImVec2& pix, int y_axis) {
// This function is convenient but should not be used to process a high volume of points. Use the Transformer structs below instead.
inline ImVec2 PlotToPixels(double x, double y, int y_axis_in = -1) {
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 int y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis;
ImVec2 pix;
if (HasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_LogScale)) {
@ -604,8 +653,7 @@ inline double NiceNum(double x, bool round) {
return nf * ImPow(10.0, expv);
}
inline void GetTicks(const ImPlotRange& range, int nMajor, int nMinor, bool logscale, ImVector<ImPlotTick> &out) {
out.shrink(0);
inline void AddDefaultTicks(const ImPlotRange& range, int nMajor, int nMinor, bool logscale, ImVector<ImPlotTick> &out) {
if (logscale) {
if (range.Min <= 0 || range.Max <= 0)
return;
@ -642,11 +690,10 @@ inline void GetTicks(const ImPlotRange& range, int nMajor, int nMinor, bool logs
}
inline void LabelTicks(ImVector<ImPlotTick> &ticks, bool scientific, ImGuiTextBuffer& buffer) {
buffer.Buf.resize(0);
char temp[32];
for (int t = 0; t < ticks.Size; t++) {
ImPlotTick *tk = &ticks[t];
if (tk->RenderLabel) {
if (tk->RenderLabel && !tk->Labeled) {
tk->TextOffset = buffer.size();
if (scientific)
sprintf(temp, "%.0e", tk->PlotPos);
@ -654,10 +701,24 @@ inline void LabelTicks(ImVector<ImPlotTick> &ticks, bool scientific, ImGuiTextBu
sprintf(temp, "%g", tk->PlotPos);
buffer.append(temp, temp + strlen(temp) + 1);
tk->Size = ImGui::CalcTextSize(buffer.Buf.Data + tk->TextOffset);
tk->Labeled = true;
}
}
}
inline void AddCustomTicks(const double* values, const char** labels, int n, ImVector<ImPlotTick>& ticks, ImGuiTextBuffer& buffer) {
for (int i = 0; i < n; ++i) {
ImPlotTick tick(values[i],false);
tick.TextOffset = buffer.size();
if (labels != NULL) {
buffer.append(labels[i], labels[i] + strlen(labels[i]) + 1);
tick.Size = ImGui::CalcTextSize(labels[i]);
tick.Labeled = true;
}
ticks.push_back(tick);
}
}
namespace {
struct AxisState {
ImPlotAxis* axis;
@ -750,7 +811,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
ImGuiContext &G = *GImGui;
ImGuiWindow * Window = G.CurrentWindow;
if (Window->SkipItems) {
gp.NextPlotData = ImPlotNextPlotData();
gp.Reset();
return false;
}
@ -797,6 +858,10 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
ImGui::BeginChild(title, ImVec2(size.x == 0 ? default_w : size.x, size.y == 0 ? default_h : size.y));
Window = ImGui::GetCurrentWindow();
Window->ScrollMax.y = 1.0f;
gp.ChildWindowMade = true;
}
else {
gp.ChildWindowMade = false;
}
ImDrawList &DrawList = *Window->DrawList;
@ -828,7 +893,9 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
y[2] = AxisState(plot.YAxis[2], gp.NextPlotData.HasYRange[2], gp.NextPlotData.YRangeCond[2],
HasFlag(plot.Flags, ImPlotFlags_YAxis3), y[1].present_so_far);
const bool lock_plot = x.lock && y[0].lock && y[1].lock && y[2].lock;
gp.LockPlot = x.lock && y[0].lock &&
( HasFlag(plot.Flags, ImPlotFlags_YAxis2) ? y[1].lock : true ) &&
( HasFlag(plot.Flags, ImPlotFlags_YAxis3) ? y[2].lock : true );
// CONSTRAINTS ------------------------------------------------------------
@ -896,10 +963,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
gp.BB_Frame = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size);
ImGui::ItemSize(gp.BB_Frame);
if (!ImGui::ItemAdd(gp.BB_Frame, 0, &gp.BB_Frame)) {
gp.NextPlotData = ImPlotNextPlotData();
gp.CurrentPlot = NULL;
if (!HasFlag(plot.Flags, ImPlotFlags_NoChild))
ImGui::EndChild();
gp.Reset();
return false;
}
gp.Hov_Frame = ImGui::ItemHoverable(gp.BB_Frame, ID);
@ -920,11 +984,11 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
}
// get ticks
if (gp.RenderX)
GetTicks(plot.XAxis.Range, plot.XAxis.Divisions, plot.XAxis.Subdivisions, HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale), gp.XTicks);
if (gp.RenderX && gp.NextPlotData.ShowDefaultTicksX)
AddDefaultTicks(plot.XAxis.Range, plot.XAxis.Divisions, plot.XAxis.Subdivisions, HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale), gp.XTicks);
for (int i = 0; i < MAX_Y_AXES; i++) {
if (gp.RenderY[i]) {
GetTicks(plot.YAxis[i].Range, plot.YAxis[i].Divisions, plot.YAxis[i].Subdivisions, HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LogScale), gp.YTicks[i]);
if (gp.RenderY[i] && gp.NextPlotData.ShowDefaultTicksY[i]) {
AddDefaultTicks(plot.YAxis[i].Range, plot.YAxis[i].Divisions, plot.YAxis[i].Subdivisions, HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LogScale), gp.YTicks[i]);
}
}
@ -1149,7 +1213,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
plot.Selecting = false;
}
// bad selection
if (plot.Selecting && (!HasFlag(plot.Flags, ImPlotFlags_BoxSelect) || lock_plot) && ImLengthSqr(plot.SelectStart - IO.MousePos) > 4) {
if (plot.Selecting && (!HasFlag(plot.Flags, ImPlotFlags_BoxSelect) || gp.LockPlot) && ImLengthSqr(plot.SelectStart - IO.MousePos) > 4) {
ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed);
}
// cancel selection
@ -1157,7 +1221,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
plot.Selecting = false;
}
// begin selection or query
if (gp.Hov_Frame && gp.Hov_Grid && IO.MouseClicked[1]) {
if (!gp.LockPlot && gp.Hov_Frame && gp.Hov_Grid && IO.MouseClicked[1]) {
plot.SelectStart = IO.MousePos;
plot.Selecting = true;
}
@ -1326,20 +1390,6 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
// push plot ID into stack
ImGui::PushID(ID);
// reset items count
gp.VisibleItemCount = 0;
// reset extents
gp.ExtentsX.Min = HUGE_VAL;
gp.ExtentsX.Max = -HUGE_VAL;
for (int i = 0; i < MAX_Y_AXES; i++) {
gp.ExtentsY[i].Min = HUGE_VAL;
gp.ExtentsY[i].Max = -HUGE_VAL;
}
// clear item names
gp.LegendLabels.Buf.resize(0);
// reset digital plot items count
gp.DigitalPlotItemCnt = 0;
gp.DigitalPlotOffset = 0;
return true;
}
@ -1515,6 +1565,7 @@ void EndPlot() {
// AXIS STATES ------------------------------------------------------------
// TODO: Move this into gp to avoid repetition
AxisState x(plot.XAxis, gp.NextPlotData.HasXRange, gp.NextPlotData.XRangeCond, true, 0);
AxisState y[MAX_Y_AXES];
y[0] = AxisState(plot.YAxis[0], gp.NextPlotData.HasYRange[0], gp.NextPlotData.YRangeCond[0], true, 0);
@ -1523,10 +1574,10 @@ void EndPlot() {
y[2] = AxisState(plot.YAxis[2], gp.NextPlotData.HasYRange[2], gp.NextPlotData.YRangeCond[2],
HasFlag(plot.Flags, ImPlotFlags_YAxis3), y[1].present_so_far);
const bool lock_plot = x.lock && y[0].lock && y[1].lock && y[2].lock;
const bool any_y_locked = y[0].lock || y[1].lock || y[2].lock;
const bool any_y_dragging = plot.YAxis[0].Dragging || plot.YAxis[1].Dragging || plot.YAxis[2].Dragging;
// FINAL RENDER -----------------------------------------------------------
// render ticks
@ -1575,20 +1626,21 @@ void EndPlot() {
// render selection/query
if (plot.Selecting) {
ImRect select_bb(ImMin(IO.MousePos, plot.SelectStart), ImMax(IO.MousePos, plot.SelectStart));
if (plot.Selecting && !lock_plot && HasFlag(plot.Flags, ImPlotFlags_BoxSelect)) {
if (IO.KeyAlt && IO.KeyShift && select_bb.GetWidth() > 2 && select_bb.GetHeight() > 2) {
bool select_big_enough = ImLengthSqr(select_bb.GetSize()) > 4;
if (plot.Selecting && !gp.LockPlot && HasFlag(plot.Flags, ImPlotFlags_BoxSelect) && select_big_enough) {
if (IO.KeyAlt && IO.KeyShift) {
DrawList.AddRectFilled(gp.BB_Grid.Min, gp.BB_Grid.Max, gp.Col_SlctBg);
DrawList.AddRect( gp.BB_Grid.Min, gp.BB_Grid.Max, gp.Col_SlctBd);
}
else if ((x.lock || IO.KeyAlt) && select_bb.GetHeight() > 2) {
else if ((x.lock || IO.KeyAlt)) {
DrawList.AddRectFilled(ImVec2(gp.BB_Grid.Min.x, select_bb.Min.y), ImVec2(gp.BB_Grid.Max.x, select_bb.Max.y), gp.Col_SlctBg);
DrawList.AddRect( ImVec2(gp.BB_Grid.Min.x, select_bb.Min.y), ImVec2(gp.BB_Grid.Max.x, select_bb.Max.y), gp.Col_SlctBd);
}
else if ((any_y_locked || IO.KeyShift) && select_bb.GetWidth() > 2) {
else if ((any_y_locked || IO.KeyShift)) {
DrawList.AddRectFilled(ImVec2(select_bb.Min.x, gp.BB_Grid.Min.y), ImVec2(select_bb.Max.x, gp.BB_Grid.Max.y), gp.Col_SlctBg);
DrawList.AddRect( ImVec2(select_bb.Min.x, gp.BB_Grid.Min.y), ImVec2(select_bb.Max.x, gp.BB_Grid.Max.y), gp.Col_SlctBd);
}
else if (select_bb.GetWidth() > 2 && select_bb.GetHeight() > 2) {
else {
DrawList.AddRectFilled(select_bb.Min, select_bb.Max, gp.Col_SlctBg);
DrawList.AddRect( select_bb.Min, select_bb.Max, gp.Col_SlctBd);
}
@ -1741,17 +1793,10 @@ void EndPlot() {
}
// CLEANUP ----------------------------------------------------------------
// Reset legend items
gp.LegendIndices.shrink(0);
// Null current plot/data
gp.CurrentPlot = NULL;
// Reset next plot data
gp.NextPlotData = ImPlotNextPlotData();
// Pop ImGui::PushID at the end of BeginPlot
ImGui::PopID();
// End child window
if (!HasFlag(plot.Flags, ImPlotFlags_NoChild))
ImGui::EndChild();
// Reset context for next plot
gp.Reset();
}
//-----------------------------------------------------------------------------
@ -1759,11 +1804,13 @@ void EndPlot() {
//-----------------------------------------------------------------------------
void SetNextPlotLimits(double x_min, double x_max, double y_min, double y_max, ImGuiCond cond) {
IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "SetNextPlotLimits() needs to be called before BeginPlot()!");
SetNextPlotLimitsX(x_min, x_max, cond);
SetNextPlotLimitsY(y_min, y_max, cond);
}
void SetNextPlotLimitsX(double x_min, double x_max, ImGuiCond cond) {
IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "SetNextPlotLSetNextPlotLimitsXimitsY() needs to be called before BeginPlot()!");
IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
gp.NextPlotData.HasXRange = true;
gp.NextPlotData.XRangeCond = cond;
@ -1772,7 +1819,8 @@ void SetNextPlotLimitsX(double x_min, double x_max, ImGuiCond cond) {
}
void SetNextPlotLimitsY(double y_min, double y_max, ImGuiCond cond, int y_axis) {
IM_ASSERT_USER_ERROR(y_axis >= 0 && y_axis < MAX_Y_AXES, "y_axis Needs to be between 0 and MAX_Y_AXES");
IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "SetNextPlotLimitsY() needs to be called before BeginPlot()!");
IM_ASSERT_USER_ERROR(y_axis >= 0 && y_axis < MAX_Y_AXES, "y_axis needs to be between 0 and MAX_Y_AXES");
IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
gp.NextPlotData.HasYRange[y_axis] = true;
gp.NextPlotData.YRangeCond[y_axis] = cond;
@ -1780,24 +1828,51 @@ void SetNextPlotLimitsY(double y_min, double y_max, ImGuiCond cond, int y_axis)
gp.NextPlotData.Y[y_axis].Max = y_max;
}
void SetNextPlotTicksX(const double* values, int n_ticks, const char** labels, bool show_default) {
IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "SetNextPlotTicksX() needs to be called before BeginPlot()!");
gp.NextPlotData.ShowDefaultTicksX = show_default;
AddCustomTicks(values, labels, n_ticks, gp.XTicks, gp.XTickLabels);
}
void SetNextPlotTicksX(double x_min, double x_max, int n_ticks, const char** labels, bool show_default) {
IM_ASSERT_USER_ERROR(n_ticks > 1, "The number of ticks must be greater than 1");
static ImVector<double> buffer;
FillRange(buffer, n_ticks, x_min, x_max);
SetNextPlotTicksX(&buffer[0], n_ticks, labels, show_default);
}
void SetNextPlotTicksY(const double* values, int n_ticks, const char** labels, bool show_default, int y_axis) {
IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "SetNextPlotTicksY() needs to be called before BeginPlot()!");
IM_ASSERT_USER_ERROR(y_axis >= 0 && y_axis < MAX_Y_AXES, "y_axis needs to be between 0 and MAX_Y_AXES");
gp.NextPlotData.ShowDefaultTicksY[y_axis] = show_default;
AddCustomTicks(values, labels, n_ticks, gp.YTicks[y_axis], gp.YTickLabels[y_axis]);
}
void SetNextPlotTicksY(double y_min, double y_max, int n_ticks, const char** labels, bool show_default, int y_axis) {
IM_ASSERT_USER_ERROR(n_ticks > 1, "The number of ticks must be greater than 1");
static ImVector<double> buffer;
FillRange(buffer, n_ticks, y_min, y_max);
SetNextPlotTicksY(&buffer[0], n_ticks, labels, show_default,y_axis);
}
void SetPlotYAxis(int y_axis) {
IM_ASSERT_USER_ERROR(y_axis >= 0 && y_axis < MAX_Y_AXES, "y_axis Needs to be between 0 and MAX_Y_AXES");
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "SetPlotYAxis() Needs to be called between BeginPlot() and EndPlot()!");
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "SetPlotYAxis() needs to be called between BeginPlot() and EndPlot()!");
IM_ASSERT_USER_ERROR(y_axis >= 0 && y_axis < MAX_Y_AXES, "y_axis needs to be between 0 and MAX_Y_AXES");
gp.CurrentPlot->CurrentYAxis = y_axis;
}
ImVec2 GetPlotPos() {
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotPos() Needs to be called between BeginPlot() and EndPlot()!");
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotPos() needs to be called between BeginPlot() and EndPlot()!");
return gp.BB_Grid.Min;
}
ImVec2 GetPlotSize() {
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotSize() Needs to be called between BeginPlot() and EndPlot()!");
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotSize() needs to be called between BeginPlot() and EndPlot()!");
return gp.BB_Grid.GetSize();
}
void PushPlotClipRect() {
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PushPlotClipRect() Needs to be called between BeginPlot() and EndPlot()!");
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PushPlotClipRect() needs to be called between BeginPlot() and EndPlot()!");
ImGui::PushClipRect(gp.BB_Grid.Min, gp.BB_Grid.Max, true);
}
@ -1806,12 +1881,12 @@ void PopPlotClipRect() {
}
bool IsPlotHovered() {
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "IsPlotHovered() Needs to be called between BeginPlot() and EndPlot()!");
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "IsPlotHovered() needs to be called between BeginPlot() and EndPlot()!");
return gp.Hov_Grid;
}
ImPlotPoint GetPlotMousePos(int y_axis_in) {
IM_ASSERT_USER_ERROR(y_axis_in >= -1 && y_axis_in < MAX_Y_AXES, "y_axis needs to between -1 and MAX_Y_AXES");
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotMousePos() Needs to be called between BeginPlot() and EndPlot()!");
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotMousePos() needs to be called between BeginPlot() and EndPlot()!");
const int y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis;
return gp.LastMousePos[y_axis];
}
@ -1819,7 +1894,7 @@ ImPlotPoint GetPlotMousePos(int y_axis_in) {
ImPlotLimits GetPlotLimits(int y_axis_in) {
IM_ASSERT_USER_ERROR(y_axis_in >= -1 && y_axis_in < MAX_Y_AXES, "y_axis needs to between -1 and MAX_Y_AXES");
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotLimits() Needs to be called between BeginPlot() and EndPlot()!");
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotLimits() needs to be called between BeginPlot() and EndPlot()!");
const int y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis;
ImPlotState& plot = *gp.CurrentPlot;
@ -1830,13 +1905,13 @@ ImPlotLimits GetPlotLimits(int y_axis_in) {
}
bool IsPlotQueried() {
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "IsPlotQueried() Needs to be called between BeginPlot() and EndPlot()!");
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "IsPlotQueried() needs to be called between BeginPlot() and EndPlot()!");
return gp.CurrentPlot->Queried;
}
ImPlotLimits GetPlotQuery(int y_axis_in) {
IM_ASSERT_USER_ERROR(y_axis_in >= -1 && y_axis_in < MAX_Y_AXES, "y_axis needs to between -1 and MAX_Y_AXES");
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotQuery() Needs to be called between BeginPlot() and EndPlot()!");
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotQuery() needs to be called between BeginPlot() and EndPlot()!");
ImPlotState& plot = *gp.CurrentPlot;
const int y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis;
@ -2301,7 +2376,7 @@ struct GetterFuncPtrImPlotPoint {
template <typename Getter>
inline void PlotEx(const char* label_id, Getter getter, int count, int offset)
{
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "Plot() Needs to be called between BeginPlot() and EndPlot()!");
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "Plot() needs to be called between BeginPlot() and EndPlot()!");
ImPlotState* plot = gp.CurrentPlot;
const int y_axis = plot->CurrentYAxis;
@ -2503,7 +2578,7 @@ struct GetterBarH {
template <typename Getter, typename TWidth>
void PlotBarsEx(const char* label_id, Getter getter, int count, TWidth width, int offset) {
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotBars() Needs to be called between BeginPlot() and EndPlot()!");
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotBars() needs to be called between BeginPlot() and EndPlot()!");
ImPlotItem* item = RegisterItem(label_id);
if (!item->Show)
@ -2593,7 +2668,7 @@ void PlotBars(const char* label_id, ImPlotPoint (*getter_func)(void* data, int i
template <typename Getter, typename THeight>
void PlotBarsHEx(const char* label_id, Getter getter, int count, THeight height, int offset) {
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotBarsH() Needs to be called between BeginPlot() and EndPlot()!");
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotBarsH() needs to be called between BeginPlot() and EndPlot()!");
ImPlotItem* item = RegisterItem(label_id);
if (!item->Show)
@ -2703,7 +2778,7 @@ struct GetterError {
template <typename Getter>
void PlotErrorBarsEx(const char* label_id, Getter getter, int count, int offset) {
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotErrorBars() Needs to be called between BeginPlot() and EndPlot()!");
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotErrorBars() needs to be called between BeginPlot() and EndPlot()!");
ImGuiID id = ImGui::GetID(label_id);
ImPlotItem* item = gp.CurrentPlot->Items.GetByKey(id);
@ -2788,7 +2863,7 @@ inline void DrawPieSlice(ImDrawList& DrawList, const ImPlotPoint& center, double
template <typename T>
void PlotPieChartEx(const char** label_ids, T* values, int count, T x, T y, T radius, bool show_percents, T angle0) {
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotPieChart() Needs to be called between BeginPlot() and EndPlot()!");
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotPieChart() needs to be called between BeginPlot() and EndPlot()!");
ImDrawList & DrawList = *ImGui::GetWindowDrawList();
T sum = 0;
@ -2893,7 +2968,7 @@ void RenderHeatmap(Transformer transformer, ImDrawList& DrawList, const T* value
template <typename T>
void PlotHeatmapEx(const char* label_id, const T* values, int rows, int cols, T scale_min, T scale_max, bool show_labels, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max) {
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotHeatmap() Needs to be called between BeginPlot() and EndPlot()!");
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotHeatmap() needs to be called between BeginPlot() and EndPlot()!");
IM_ASSERT_USER_ERROR(scale_min != scale_max, "Scale values must be different!");
ImPlotItem* item = RegisterItem(label_id);
if (!item->Show)
@ -2938,7 +3013,7 @@ void PlotHeatmap(const char* label_id, const double* values, int rows, int cols,
template <typename Getter>
inline void PlotDigitalEx(const char* label_id, Getter getter, int count, int offset)
{
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotDigital() Needs to be called between BeginPlot() and EndPlot()!");
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotDigital() needs to be called between BeginPlot() and EndPlot()!");
ImPlotItem* item = RegisterItem(label_id);
if (!item->Show)
@ -3044,7 +3119,7 @@ void PlotText(const char* text, float x, float y, bool vertical, const ImVec2& p
//-----------------------------------------------------------------------------
// double
void PlotText(const char* text, double x, double y, bool vertical, const ImVec2& pixel_offset) {
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotText() Needs to be called between BeginPlot() and EndPlot()!");
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotText() needs to be called between BeginPlot() and EndPlot()!");
ImDrawList & DrawList = *ImGui::GetWindowDrawList();
PushPlotClipRect();
ImVec2 pos = PlotToPixels(ImPlotPoint(x,y)) + pixel_offset;
@ -3099,7 +3174,9 @@ void ShowColormapScale(double scale_min, double scale_max, float height) {
ImPlotRange range;
range.Min = scale_min;
range.Max = scale_max;
GetTicks(range, 10, 0, false, ticks);
ticks.shrink(0);
txt_buff.Buf.shrink(0);
AddDefaultTicks(range, 10, 0, false, ticks);
LabelTicks(ticks, false, txt_buff);
float max_width = 0;
for (int i = 0; i < ticks.Size; ++i)

View File

@ -313,6 +313,13 @@ void SetNextPlotLimitsX(double x_min, double x_max, ImGuiCond cond = ImGuiCond_O
// Set the Y axis range limits of the next plot. Call right before BeginPlot(). If ImGuiCond_Always is used, the Y axis limits will be locked.
void SetNextPlotLimitsY(double y_min, double y_max, ImGuiCond cond = ImGuiCond_Once, int y_axis = 0);
// Set the X axis ticks and optionally the labels for the next plot.
void SetNextPlotTicksX(const double* values, int n_ticks, const char** labels = NULL, bool show_default = false);
void SetNextPlotTicksX(double x_min, double x_max, int n_ticks, const char** labels = NULL, bool show_default = false);
// Set the Y axis ticks and optionally the labels for the next plot.
void SetNextPlotTicksY(const double* values, int n_ticks, const char** labels = NULL, bool show_default = false, int y_axis = 0);
void SetNextPlotTicksY(double y_min, double y_max, int n_ticks, const char** labels = NULL, bool show_default = false, int y_axis = 0);
// Select which Y axis will be used for subsequent plot elements. The default is '0', or the first (left) Y axis.
void SetPlotYAxis(int y_axis);

View File

@ -241,12 +241,18 @@ void ShowDemoWindow(bool* p_open) {
}
//-------------------------------------------------------------------------
if (ImGui::CollapsingHeader("Bar Plots")) {
static const char* labels[] = {"S1","S2","S3","S4","S5","S6","S7","S8","S9","S10"};
static const double positions[] = {0,1,2,3,4,5,6,7,8,9};
static bool horz = false;
ImGui::Checkbox("Horizontal",&horz);
if (horz)
if (horz) {
ImPlot::SetNextPlotLimits(0, 110, -0.5, 9.5, ImGuiCond_Always);
else
ImPlot::SetNextPlotTicksY(positions, 10, labels);
}
else {
ImPlot::SetNextPlotLimits(-0.5, 9.5, 0, 110, ImGuiCond_Always);
ImPlot::SetNextPlotTicksX(positions, 10, labels);
}
if (ImPlot::BeginPlot("Bar Plot", horz ? "Score": "Student", horz ? "Student" : "Score")) {
static t_float midtm[10] = {83, 67, 23, 89, 83, 78, 91, 82, 85, 90};
static t_float final[10] = {80, 62, 56, 99, 55, 78, 88, 78, 90, 100};
@ -331,15 +337,21 @@ void ShowDemoWindow(bool* p_open) {
values2[i] = RandomRange(0,1);
}
static ImPlotColormap map = ImPlotColormap_Viridis;
if (ImGui::Button("Cycle Colormap",ImVec2(225,0)))
if (ImGui::Button("Change Colormap",ImVec2(225,0)))
map = (map + 1) % ImPlotColormap_COUNT;
ImPlot::SetColormap(map);
ImGui::SameLine();
ImGui::LabelText("##Colormap Index", cmap_names[map]);
ImGui::SetNextItemWidth(225);
ImGui::DragFloat("Max",&scale_max,0.01f,0.1f,20);
static ImPlotAxisFlags axes_flags = ImPlotAxisFlags_LockMin | ImPlotAxisFlags_LockMax;
if (ImPlot::BeginPlot("##Heatmap1",NULL,NULL,ImVec2(225,225),ImPlotFlags_ContextMenu,axes_flags,axes_flags)) {
static ImPlotAxisFlags axes_flags = ImPlotAxisFlags_LockMin | ImPlotAxisFlags_LockMax | ImPlotAxisFlags_TickLabels;
static const char* xlabels[] = {"C1","C2","C3","C4","C5","C6","C7"};
static const char* ylabels[] = {"R1","R2","R3","R4","R5","R6","R7"};
SetNextPlotTicksX(0 + 1.0/14.0, 1 - 1.0/14.0, 7, xlabels);
SetNextPlotTicksY(1- 1.0/14.0, 0 + 1.0/14.0, 7, ylabels);
if (ImPlot::BeginPlot("##Heatmap1",NULL,NULL,ImVec2(225,225),0,axes_flags,axes_flags)) {
ImPlot::PlotHeatmap("heat",values1[0],7,7,scale_min,scale_max);
ImPlot::EndPlot();
}
@ -388,11 +400,11 @@ void ShowDemoWindow(bool* p_open) {
//-------------------------------------------------------------------------
if (ImGui::CollapsingHeader("Markers and Text")) {
static ImPlotColormap map = ImPlotColormap_Default;
if (ImGui::Button("Cycle Colormap##2"))
if (ImGui::Button("Change Colormap##2"))
map = (map + 1) % ImPlotColormap_COUNT;
ImGui::SameLine();
ImGui::LabelText("##Colormap Index", cmap_names[map]);
ImGui::PushID(map); // NB: The merely a workaround so that the demo can cycle color maps. You wouldn't need to do this in your own code!
ImGui::PushID(map); // NB: This is merely a workaround so that the demo can cycle color maps. You wouldn't need to do this in your own code!
ImPlot::SetNextPlotLimits(0, 10, 0, 12);
if (ImPlot::BeginPlot("##MarkerStyles", NULL, NULL, ImVec2(-1,0), 0, 0, 0)) {
ImPlot::SetColormap(map);
@ -830,6 +842,33 @@ void ShowDemoWindow(bool* p_open) {
}
}
//-------------------------------------------------------------------------
if (ImGui::CollapsingHeader("Custom Ticks")) {
static bool custom_ticks = true;
static bool custom_labels = true;
ImGui::Checkbox("Show Custom Ticks", &custom_ticks);
if (custom_ticks) {
ImGui::SameLine();
ImGui::Checkbox("Show Custom Labels", &custom_labels);
}
double pi = 3.14;
const char* pi_str[] = {"PI"};
static double yticks[] = {1,3,7,9};
static const char* ylabels[] = {"One","Three","Seven","Nine"};
static double yticks_aux[] = {0.2,0.4,0.6};
static const char* ylabels_aux[] = {"A","B","C","D","E","F"};
if (custom_ticks) {
ImPlot::SetNextPlotTicksX(&pi,1,custom_labels ? pi_str : NULL, true);
ImPlot::SetNextPlotTicksY(yticks, 4, custom_labels ? ylabels : NULL);
ImPlot::SetNextPlotTicksY(yticks_aux, 3, custom_labels ? ylabels_aux : NULL, false, 1);
ImPlot::SetNextPlotTicksY(0, 1, 6, custom_labels ? ylabels_aux : NULL, false, 2);
}
ImPlot::SetNextPlotLimits(2.5,5,0,10);
if (ImPlot::BeginPlot("Custom Ticks", NULL, NULL, ImVec2(-1,0), ImPlotFlags_Default | ImPlotFlags_YAxis2 | ImPlotFlags_YAxis3)) {
ImPlot::EndPlot();
}
}
//-------------------------------------------------------------------------
if (ImGui::CollapsingHeader("Custom Styles")) {
static ImVec4 my_map[3] = {
ImVec4(0.000f, 0.980f, 0.604f, 1.0f),