diff --git a/implot.cpp b/implot.cpp index 2b26980..85b66bf 100644 --- a/implot.cpp +++ b/implot.cpp @@ -254,23 +254,7 @@ struct ImPlotTick { bool Labeled; }; -struct ImPlotItem { - ImPlotItem() { - Show = true; - Highlight = false; - Color = NextColor(); - NameOffset = -1; - ID = 0; - } - ~ImPlotItem() { ID = 0; } - bool Show; - bool Highlight; - ImVec4 Color; - int NameOffset; - ImGuiID ID; -}; - -/// Plot axis structure. You shouldn't need to construct this! +/// Axis state information that must persist after EndPlot struct ImPlotAxis { ImPlotAxis() { Dragging = false; @@ -287,7 +271,64 @@ struct ImPlotAxis { ImPlotAxisFlags Flags, PreviousFlags; }; -/// Holds Plot state information that must persist between frames +/// Axis state information only needed between BeginPlot/EndPlot +struct ImPlotAxisState { + ImPlotAxis* axis; + bool has_range; + ImGuiCond range_cond; + bool present; + int present_so_far; + bool invert; + bool lock_min; + bool lock_max; + bool lock; + ImPlotAxisState(ImPlotAxis& axis_in, bool has_range_in, ImGuiCond range_cond_in, + bool present_in, int previous_present) + : axis(&axis_in), + has_range(has_range_in), + range_cond(range_cond_in), + present(present_in), + present_so_far(previous_present + (present ? 1 : 0)), + invert(HasFlag(axis->Flags, ImPlotAxisFlags_Invert)), + lock_min(HasFlag(axis->Flags, ImPlotAxisFlags_LockMin) || (has_range && range_cond == ImGuiCond_Always)), + lock_max(HasFlag(axis->Flags, ImPlotAxisFlags_LockMax) || (has_range && range_cond == ImGuiCond_Always)), + lock(!present || ((lock_min && lock_max) || (has_range && range_cond == ImGuiCond_Always))) {} + + ImPlotAxisState() + : axis(), + has_range(), + range_cond(), + present(), + present_so_far(), + invert(), + lock_min(), + lock_max(), + lock() {} +}; + +struct ImPlotAxisColor { + ImPlotAxisColor() : Major(), Minor(), Txt() {} + ImU32 Major, Minor, Txt; +}; + +/// State information for Plot items +struct ImPlotItem { + ImPlotItem() { + Show = true; + Highlight = false; + Color = NextColor(); + NameOffset = -1; + ID = 0; + } + ~ImPlotItem() { ID = 0; } + bool Show; + bool Highlight; + ImVec4 Color; + int NameOffset; + ImGuiID ID; +}; + +/// Holds Plot state information that must persist after EndPlot struct ImPlotState { ImPlotState() { Selecting = Querying = Queried = DraggingQuery = false; @@ -393,12 +434,10 @@ struct ImPlotContext { Col_Txt, Col_TxtDis, Col_SlctBg, Col_SlctBd, Col_QryBg, Col_QryBd; - struct AxisColor { - AxisColor() : Major(), Minor(), Txt() {} - ImU32 Major, Minor, Txt; - }; - AxisColor Col_X; - AxisColor Col_Y[MAX_Y_AXES]; + ImPlotAxisColor Col_X; + ImPlotAxisColor Col_Y[MAX_Y_AXES]; + ImPlotAxisState X; + ImPlotAxisState Y[MAX_Y_AXES]; // Tick marks ImVector XTicks, YTicks[MAX_Y_AXES]; ImGuiTextBuffer XTickLabels, YTickLabels[MAX_Y_AXES]; @@ -720,42 +759,8 @@ inline void AddCustomTicks(const double* values, const char** labels, int n, ImV } namespace { -struct AxisState { - ImPlotAxis* axis; - bool has_range; - ImGuiCond range_cond; - bool present; - int present_so_far; - bool flip; - bool lock_min; - bool lock_max; - bool lock; - AxisState(ImPlotAxis& axis_in, bool has_range_in, ImGuiCond range_cond_in, - bool present_in, int previous_present) - : axis(&axis_in), - has_range(has_range_in), - range_cond(range_cond_in), - present(present_in), - present_so_far(previous_present + (present ? 1 : 0)), - flip(HasFlag(axis->Flags, ImPlotAxisFlags_Invert)), - lock_min(HasFlag(axis->Flags, ImPlotAxisFlags_LockMin)), - lock_max(HasFlag(axis->Flags, ImPlotAxisFlags_LockMax)), - lock(present && ((lock_min && lock_max) || (has_range && range_cond == ImGuiCond_Always))) {} - - AxisState() - : axis(), - has_range(), - range_cond(), - present(), - present_so_far(), - flip(), - lock_min(), - lock_max(), - lock() {} -}; - -void UpdateAxisColor(int axis_flag, ImPlotContext::AxisColor* col) { +void UpdateAxisColor(int axis_flag, ImPlotAxisColor* col) { const ImVec4 col_Axis = gp.Style.Colors[axis_flag].w == -1 ? ImGui::GetStyle().Colors[ImGuiCol_Text] * ImVec4(1, 1, 1, 0.25f) : gp.Style.Colors[axis_flag]; col->Major = ImGui::GetColorU32(col_Axis); col->Minor = ImGui::GetColorU32(col_Axis * ImVec4(1, 1, 1, 0.25f)); @@ -772,16 +777,16 @@ struct ImPlotAxisScale { class YPadCalculator { public: - YPadCalculator(const AxisState* axis_states, const float* max_label_widths, float txt_off) - : AxisStates(axis_states), MaxLabelWidths(max_label_widths), TxtOff(txt_off) {} + YPadCalculator(const ImPlotAxisState* axis_states, const float* max_label_widths, float txt_off) + : ImPlotAxisStates(axis_states), MaxLabelWidths(max_label_widths), TxtOff(txt_off) {} float operator()(int y_axis) { ImPlotState& plot = *gp.CurrentPlot; - if (!AxisStates[y_axis].present) { return 0; } + if (!ImPlotAxisStates[y_axis].present) { return 0; } // If we have more than 1 axis present before us, then we need // extra space to account for our tick bar. float pad_result = 0; - if (AxisStates[y_axis].present_so_far >= 3) { + if (ImPlotAxisStates[y_axis].present_so_far >= 3) { pad_result += 6.0f; } if (!HasFlag(plot.YAxis[y_axis].Flags, ImPlotAxisFlags_TickLabels)) { @@ -792,7 +797,7 @@ class YPadCalculator { } private: - const AxisState* const AxisStates; + const ImPlotAxisState* const ImPlotAxisStates; const float* const MaxLabelWidths; const float TxtOff; }; @@ -885,17 +890,14 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons } // AXIS STATES ------------------------------------------------------------ - 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); - y[1] = AxisState(plot.YAxis[1], gp.NextPlotData.HasYRange[1], gp.NextPlotData.YRangeCond[1], - HasFlag(plot.Flags, ImPlotFlags_YAxis2), y[0].present_so_far); - y[2] = AxisState(plot.YAxis[2], gp.NextPlotData.HasYRange[2], gp.NextPlotData.YRangeCond[2], - HasFlag(plot.Flags, ImPlotFlags_YAxis3), y[1].present_so_far); + gp.X = ImPlotAxisState(plot.XAxis, gp.NextPlotData.HasXRange, gp.NextPlotData.XRangeCond, true, 0); + gp.Y[0] = ImPlotAxisState(plot.YAxis[0], gp.NextPlotData.HasYRange[0], gp.NextPlotData.YRangeCond[0], true, 0); + gp.Y[1] = ImPlotAxisState(plot.YAxis[1], gp.NextPlotData.HasYRange[1], gp.NextPlotData.YRangeCond[1], + HasFlag(plot.Flags, ImPlotFlags_YAxis2), gp.Y[0].present_so_far); + gp.Y[2] = ImPlotAxisState(plot.YAxis[2], gp.NextPlotData.HasYRange[2], gp.NextPlotData.YRangeCond[2], + HasFlag(plot.Flags, ImPlotFlags_YAxis3), gp.Y[1].present_so_far); - 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 ); + gp.LockPlot = gp.X.lock && gp.Y[0].lock && gp.Y[1].lock && gp.Y[2].lock; // CONSTRAINTS ------------------------------------------------------------ @@ -977,7 +979,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_TickLabels)) && plot.XAxis.Divisions > 1; for (int i = 0; i < MAX_Y_AXES; i++) { gp.RenderY[i] = - y[i].present && + gp.Y[i].present && (HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_GridLines) || HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_TickMarks) || HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_TickLabels)) && plot.YAxis[i].Divisions > 1; @@ -998,7 +1000,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons float max_label_width[MAX_Y_AXES] = {}; for (int i = 0; i < MAX_Y_AXES; i++) { - if (y[i].present && HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_TickLabels)) { + if (gp.Y[i].present && HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_TickLabels)) { LabelTicks(gp.YTicks[i], HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_Scientific), gp.YTickLabels[i]); for (int t = 0; t < gp.YTicks[i].Size; t++) { ImPlotTick *yt = &gp.YTicks[i][t]; @@ -1013,7 +1015,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons const float txt_height = ImGui::GetTextLineHeight(); const float pad_top = title_size.x > 0.0f ? txt_height + txt_off : 0; const float pad_bot = (HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_TickLabels) ? txt_height + txt_off : 0) + (x_label ? txt_height + txt_off : 0); - YPadCalculator y_axis_pad(y, max_label_width, txt_off); + YPadCalculator y_axis_pad(gp.Y, max_label_width, txt_off); const float pad_left = y_axis_pad(0) + (y_label ? txt_height + txt_off : 0); const float pad_right = y_axis_pad(1) + y_axis_pad(2); gp.BB_Grid = ImRect(gp.BB_Canvas.Min + ImVec2(pad_left, pad_top), gp.BB_Canvas.Max - ImVec2(pad_right, pad_bot)); @@ -1031,7 +1033,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons // The third axis may be either referenced to the right of the // bounding box, or 6 pixels further past the end of the 2nd axis. gp.AxisLabelReference[2] = - !y[1].present ? + !gp.Y[1].present ? gp.BB_Grid.Max.x : (gp.AxisLabelReference[1] + y_axis_pad(1) + 6); @@ -1047,9 +1049,9 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons ImVec2(gp.BB_Grid.Max.x - 6, gp.BB_Grid.Max.y)); const bool hov_y_axis_region[MAX_Y_AXES] = { - y[0].present && (yAxisRegion_bb[0].Contains(IO.MousePos) || centralRegion.Contains(IO.MousePos)), - y[1].present && (yAxisRegion_bb[1].Contains(IO.MousePos) || centralRegion.Contains(IO.MousePos)), - y[2].present && (yAxisRegion_bb[2].Contains(IO.MousePos) || centralRegion.Contains(IO.MousePos)), + gp.Y[0].present && (yAxisRegion_bb[0].Contains(IO.MousePos) || centralRegion.Contains(IO.MousePos)), + gp.Y[1].present && (yAxisRegion_bb[1].Contains(IO.MousePos) || centralRegion.Contains(IO.MousePos)), + gp.Y[2].present && (yAxisRegion_bb[2].Contains(IO.MousePos) || centralRegion.Contains(IO.MousePos)), }; const bool any_hov_y_axis_region = hov_y_axis_region[0] || hov_y_axis_region[1] || hov_y_axis_region[2]; @@ -1085,7 +1087,6 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons // DRAG INPUT ------------------------------------------------------------- - // end drags if (plot.XAxis.Dragging && (IO.MouseReleased[0] || !IO.MouseDown[0])) { plot.XAxis.Dragging = false; @@ -1102,33 +1103,33 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons // do drag if (drag_in_progress) { UpdateTransformCache(); - if (!x.lock && plot.XAxis.Dragging) { + if (!gp.X.lock && plot.XAxis.Dragging) { ImPlotPoint plot_tl = PixelsToPlot(gp.BB_Grid.Min - IO.MouseDelta, 0); ImPlotPoint plot_br = PixelsToPlot(gp.BB_Grid.Max - IO.MouseDelta, 0); - if (!x.lock_min) - plot.XAxis.Range.Min = x.flip ? plot_br.x : plot_tl.x; - if (!x.lock_max) - plot.XAxis.Range.Max = x.flip ? plot_tl.x : plot_br.x; + if (!gp.X.lock_min) + plot.XAxis.Range.Min = gp.X.invert ? plot_br.x : plot_tl.x; + if (!gp.X.lock_max) + plot.XAxis.Range.Max = gp.X.invert ? plot_tl.x : plot_br.x; } for (int i = 0; i < MAX_Y_AXES; i++) { - if (!y[i].lock && plot.YAxis[i].Dragging) { + if (!gp.Y[i].lock && plot.YAxis[i].Dragging) { ImPlotPoint plot_tl = PixelsToPlot(gp.BB_Grid.Min - IO.MouseDelta, i); ImPlotPoint plot_br = PixelsToPlot(gp.BB_Grid.Max - IO.MouseDelta, i); - if (!y[i].lock_min) - plot.YAxis[i].Range.Min = y[i].flip ? plot_tl.y : plot_br.y; - if (!y[i].lock_max) - plot.YAxis[i].Range.Max = y[i].flip ? plot_br.y : plot_tl.y; + if (!gp.Y[i].lock_min) + plot.YAxis[i].Range.Min = gp.Y[i].invert ? plot_tl.y : plot_br.y; + if (!gp.Y[i].lock_max) + plot.YAxis[i].Range.Max = gp.Y[i].invert ? plot_br.y : plot_tl.y; } } // Set the mouse cursor based on which axes are moving. int direction = 0; - if (!x.lock && plot.XAxis.Dragging) { + if (!gp.X.lock && plot.XAxis.Dragging) { direction |= (1 << 1); } for (int i = 0; i < MAX_Y_AXES; i++) { - if (!y[i].present) { continue; } - if (!y[i].lock && plot.YAxis[i].Dragging) { + if (!gp.Y[i].present) { continue; } + if (!gp.Y[i].lock && plot.YAxis[i].Dragging) { direction |= (1 << 2); break; } @@ -1165,25 +1166,25 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons zoom_rate = (-zoom_rate) / (1.0f + (2.0f * zoom_rate)); float tx = Remap(IO.MousePos.x, gp.BB_Grid.Min.x, gp.BB_Grid.Max.x, 0.0f, 1.0f); float ty = Remap(IO.MousePos.y, gp.BB_Grid.Min.y, gp.BB_Grid.Max.y, 0.0f, 1.0f); - if (hov_x_axis_region && !x.lock) { + if (hov_x_axis_region && !gp.X.lock) { ImPlotAxisScale axis_scale(0, tx, ty, zoom_rate); const ImPlotPoint& plot_tl = axis_scale.Min; const ImPlotPoint& plot_br = axis_scale.Max; - if (!x.lock_min) - plot.XAxis.Range.Min = x.flip ? plot_br.x : plot_tl.x; - if (!x.lock_max) - plot.XAxis.Range.Max = x.flip ? plot_tl.x : plot_br.x; + if (!gp.X.lock_min) + plot.XAxis.Range.Min = gp.X.invert ? plot_br.x : plot_tl.x; + if (!gp.X.lock_max) + plot.XAxis.Range.Max = gp.X.invert ? plot_tl.x : plot_br.x; } for (int i = 0; i < MAX_Y_AXES; i++) { - if (hov_y_axis_region[i] && !y[i].lock) { + if (hov_y_axis_region[i] && !gp.Y[i].lock) { ImPlotAxisScale axis_scale(i, tx, ty, zoom_rate); const ImPlotPoint& plot_tl = axis_scale.Min; const ImPlotPoint& plot_br = axis_scale.Max; - if (!y[i].lock_min) - plot.YAxis[i].Range.Min = y[i].flip ? plot_tl.y : plot_br.y; - if (!y[i].lock_max) - plot.YAxis[i].Range.Max = y[i].flip ? plot_br.y : plot_tl.y; + if (!gp.Y[i].lock_min) + plot.YAxis[i].Range.Min = gp.Y[i].invert ? plot_tl.y : plot_br.y; + if (!gp.Y[i].lock_max) + plot.YAxis[i].Range.Max = gp.Y[i].invert ? plot_br.y : plot_tl.y; } } } @@ -1197,16 +1198,16 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons if (HasFlag(plot.Flags, ImPlotFlags_BoxSelect) && ImFabs(select_size.x) > 2 && ImFabs(select_size.y) > 2) { ImPlotPoint p1 = PixelsToPlot(plot.SelectStart); ImPlotPoint p2 = PixelsToPlot(IO.MousePos); - if (!x.lock_min && !IO.KeyAlt) + if (!gp.X.lock_min && !IO.KeyAlt) plot.XAxis.Range.Min = ImMin(p1.x, p2.x); - if (!x.lock_max && !IO.KeyAlt) + if (!gp.X.lock_max && !IO.KeyAlt) plot.XAxis.Range.Max = ImMax(p1.x, p2.x); for (int i = 0; i < MAX_Y_AXES; i++) { p1 = PixelsToPlot(plot.SelectStart, i); p2 = PixelsToPlot(IO.MousePos, i); - if (!y[i].lock_min && !IO.KeyShift) + if (!gp.Y[i].lock_min && !IO.KeyShift) plot.YAxis[i].Range.Min = ImMin(p1.y, p2.y); - if (!y[i].lock_max && !IO.KeyShift) + if (!gp.Y[i].lock_max && !IO.KeyShift) plot.YAxis[i].Range.Max = ImMax(p1.y, p2.y); } } @@ -1221,7 +1222,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons plot.Selecting = false; } // begin selection or query - if (!gp.LockPlot && gp.Hov_Frame && gp.Hov_Grid && IO.MouseClicked[1]) { + if (gp.Hov_Frame && gp.Hov_Grid && IO.MouseClicked[1]) { plot.SelectStart = IO.MousePos; plot.Selecting = true; } @@ -1331,7 +1332,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons } for (int i = 0; i < MAX_Y_AXES; i++) { - if (y[i].present && HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_GridLines)) { + if (gp.Y[i].present && HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_GridLines)) { for (int t = 0; t < gp.YTicks[i].Size; t++) { ImPlotTick *yt = &gp.YTicks[i][t]; DrawList.AddLine(ImVec2(gp.BB_Grid.Min.x, yt->PixelPos), ImVec2(gp.BB_Grid.Max.x, yt->PixelPos), yt->Major ? gp.Col_Y[i].Major : gp.Col_Y[i].Minor, 1); @@ -1364,7 +1365,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons } ImGui::PushClipRect(gp.BB_Frame.Min, gp.BB_Frame.Max, true); for (int i = 0; i < MAX_Y_AXES; i++) { - if (y[i].present && HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_TickLabels)) { + if (gp.Y[i].present && HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_TickLabels)) { const float x_start = gp.AxisLabelReference[i] + ((i == 0) ? @@ -1412,40 +1413,51 @@ bool DragFloat(const char* label, float* v, float v_speed, float v_min, f return ImGui::DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, "%.3f", 1); } -inline void AxisMenu(ImPlotAxis& Axis) { - ImGui::PushItemWidth(75); - bool lock_min = HasFlag(Axis.Flags, ImPlotAxisFlags_LockMin); - bool lock_max = HasFlag(Axis.Flags, ImPlotAxisFlags_LockMax); - bool invert = HasFlag(Axis.Flags, ImPlotAxisFlags_Invert); +inline void BeginDisabledControls(bool cond) { + if (cond) { + ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.25f); + } +} + +inline void EndDisabledControls(bool cond) { + if (cond) { + ImGui::PopItemFlag(); + ImGui::PopStyleVar(); + } +} + +inline void AxisMenu(ImPlotAxis& Axis, ImPlotAxisState& state) { + ImGui::PushItemWidth(75); + bool total_lock = state.has_range && state.range_cond == ImGuiCond_Always; bool logscale = HasFlag(Axis.Flags, ImPlotAxisFlags_LogScale); bool grid = HasFlag(Axis.Flags, ImPlotAxisFlags_GridLines); bool ticks = HasFlag(Axis.Flags, ImPlotAxisFlags_TickMarks); bool labels = HasFlag(Axis.Flags, ImPlotAxisFlags_TickLabels); - if (ImGui::Checkbox("##LockMin", &lock_min)) - FlipFlag(Axis.Flags, ImPlotAxisFlags_LockMin); - ImGui::SameLine(); - if (lock_min) { - ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); - ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.25f); - } - DragFloat("Min", &Axis.Range.Min, 0.01f + 0.01f * (float)Axis.Range.Size(), -HUGE_VAL, Axis.Range.Max - DBL_EPSILON); - if (lock_min) { - ImGui::PopItemFlag(); - ImGui::PopStyleVar(); } - if (ImGui::Checkbox("##LockMax", &lock_max)) - FlipFlag(Axis.Flags, ImPlotAxisFlags_LockMax); + BeginDisabledControls(total_lock); + if (ImGui::Checkbox("##LockMin", &state.lock_min)) + FlipFlag(Axis.Flags, ImPlotAxisFlags_LockMin); + EndDisabledControls(total_lock); + ImGui::SameLine(); - if (lock_max) { - ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); - ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.25f); } + BeginDisabledControls(state.lock_min); + DragFloat("Min", &Axis.Range.Min, 0.01f + 0.01f * (float)Axis.Range.Size(), -HUGE_VAL, Axis.Range.Max - DBL_EPSILON); + EndDisabledControls(state.lock_min); + + BeginDisabledControls(total_lock); + if (ImGui::Checkbox("##LockMax", &state.lock_max)) + FlipFlag(Axis.Flags, ImPlotAxisFlags_LockMax); + EndDisabledControls(total_lock); + + ImGui::SameLine(); + BeginDisabledControls(state.lock_max); DragFloat("Max", &Axis.Range.Max, 0.01f + 0.01f * (float)Axis.Range.Size(), Axis.Range.Min + DBL_EPSILON, HUGE_VAL); - if (lock_max) { - ImGui::PopItemFlag(); - ImGui::PopStyleVar(); - } + EndDisabledControls(state.lock_max); + ImGui::Separator(); - if (ImGui::Checkbox("Invert", &invert)) + + if (ImGui::Checkbox("Invert", &state.invert)) FlipFlag(Axis.Flags, ImPlotAxisFlags_Invert); if (ImGui::Checkbox("Log Scale", &logscale)) FlipFlag(Axis.Flags, ImPlotAxisFlags_LogScale); @@ -1456,12 +1468,13 @@ inline void AxisMenu(ImPlotAxis& Axis) { FlipFlag(Axis.Flags, ImPlotAxisFlags_TickMarks); if (ImGui::Checkbox("Labels", &labels)) FlipFlag(Axis.Flags, ImPlotAxisFlags_TickLabels); + } void PlotContextMenu(ImPlotState& plot) { if (ImGui::BeginMenu("X-Axis")) { ImGui::PushID("X"); - AxisMenu(plot.XAxis); + AxisMenu(plot.XAxis, gp.X); ImGui::PopID(); ImGui::EndMenu(); } @@ -1480,7 +1493,7 @@ void PlotContextMenu(ImPlotState& plot) { } if (ImGui::BeginMenu(buf)) { ImGui::PushID(i); - AxisMenu(plot.YAxis[i]); + AxisMenu(plot.YAxis[i], gp.Y[i]); ImGui::PopID(); ImGui::EndMenu(); } @@ -1565,16 +1578,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); - y[1] = AxisState(plot.YAxis[1], gp.NextPlotData.HasYRange[1], gp.NextPlotData.YRangeCond[1], - HasFlag(plot.Flags, ImPlotFlags_YAxis2), y[0].present_so_far); - 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 any_y_locked = y[0].lock || y[1].lock || y[2].lock; + const bool any_y_locked = gp.Y[0].lock || gp.Y[1].present ? gp.Y[1].lock : false || gp.Y[2].present ? gp.Y[2].lock : false; const bool any_y_dragging = plot.YAxis[0].Dragging || plot.YAxis[1].Dragging || plot.YAxis[2].Dragging; @@ -1593,7 +1597,7 @@ void EndPlot() { ImGui::PushClipRect(gp.BB_Grid.Min, ImVec2(gp.BB_Frame.Max.x, gp.BB_Grid.Max.y), true); int axis_count = 0; for (int i = 0; i < MAX_Y_AXES; i++) { - if (!y[i].present) { continue; } + if (!gp.Y[i].present) { continue; } axis_count++; if (!HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_TickMarks)) { continue; } @@ -1632,7 +1636,7 @@ void EndPlot() { 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)) { + else if ((gp.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); } @@ -2402,6 +2406,12 @@ inline void PlotEx(const char* label_id, Getter getter, int count, int offset) ImPlotPoint p = getter(i); FitPoint(p); } + if (rend_fill) { + ImPlotPoint p1 = getter(0); + ImPlotPoint p2 = getter(count - 1); + p1.y = 0; p2.y = 0; + FitPoint(p1); FitPoint(p2); + } } PushPlotClipRect(); // render fill diff --git a/implot_demo.cpp b/implot_demo.cpp index a42de28..1523037 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -67,7 +67,7 @@ struct ScrollingData { int Offset; ImVector Data; ScrollingData() { - MaxSize = 1000; + MaxSize = 2000; Offset = 0; Data.reserve(MaxSize); } @@ -93,7 +93,7 @@ struct RollingData { ImVector Data; RollingData() { Span = 10.0f; - Data.reserve(1000); + Data.reserve(2000); } void AddPoint(t_float x, t_float y) { t_float xmod = Fmod(x, Span); @@ -375,14 +375,20 @@ void ShowDemoWindow(bool* p_open) { sdata2.AddPoint(t, mouse.y * 0.0005f); rdata2.AddPoint(t, mouse.y * 0.0005f); } - ImPlot::SetNextPlotLimitsX(t - 10, t, paused ? ImGuiCond_Once : ImGuiCond_Always); + static float history = 10.0f; + ImGui::SliderFloat("History",&history,1,30,"%.1f s"); + rdata1.Span = history; + rdata2.Span = history; + ImPlot::SetNextPlotLimitsX(t - history, t, paused ? ImGuiCond_Once : ImGuiCond_Always); static int rt_axis = ImPlotAxisFlags_Default & ~ImPlotAxisFlags_TickLabels; - if (ImPlot::BeginPlot("##Scrolling", NULL, NULL, ImVec2(-1,150), ImPlotFlags_Default, rt_axis, rt_axis)) { + if (ImPlot::BeginPlot("##Scrolling", NULL, NULL, ImVec2(-1,150), ImPlotFlags_Default, rt_axis, rt_axis | ImPlotAxisFlags_LockMin)) { ImPlot::PlotLine("Data 1", &sdata1.Data[0].x, &sdata1.Data[0].y, sdata1.Data.size(), sdata1.Offset, 2 * sizeof(t_float)); + ImPlot::PushStyleColor(ImPlotCol_Fill, ImVec4(1,0,0,0.25f)); ImPlot::PlotLine("Data 2", &sdata2.Data[0].x, &sdata2.Data[0].y, sdata2.Data.size(), sdata2.Offset, 2 * sizeof(t_float)); + ImPlot::PopStyleColor(); ImPlot::EndPlot(); } - ImPlot::SetNextPlotLimitsX(0, 10, ImGuiCond_Always); + ImPlot::SetNextPlotLimitsX(0, history, ImGuiCond_Always); if (ImPlot::BeginPlot("##Rolling", NULL, NULL, ImVec2(-1,150), ImPlotFlags_Default, rt_axis, rt_axis)) { ImPlot::PlotLine("Data 1", &rdata1.Data[0].x, &rdata1.Data[0].y, rdata1.Data.size(), 0, 2 * sizeof(t_float)); ImPlot::PlotLine("Data 2", &rdata2.Data[0].x, &rdata2.Data[0].y, rdata2.Data.size(), 0, 2 * sizeof(t_float)); @@ -632,13 +638,14 @@ void ShowDemoWindow(bool* p_open) { } //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Drag and Drop")) { + const int K_CHANNELS = 9; srand((int)(10000000 * ImGui::GetTime())); static bool paused = false; static bool init = true; - static ScrollingData data[10]; - static bool show[10]; + static ScrollingData data[K_CHANNELS]; + static bool show[K_CHANNELS]; if (init) { - for (int i = 0; i < 10; ++i) { + for (int i = 0; i < K_CHANNELS; ++i) { show[i] = false; } init = false; @@ -646,7 +653,7 @@ void ShowDemoWindow(bool* p_open) { ImGui::BulletText("Drag data items from the left column onto the plot."); ImGui::BeginGroup(); if (ImGui::Button("Clear", ImVec2(100, 0))) { - for (int i = 0; i < 10; ++i) { + for (int i = 0; i < K_CHANNELS; ++i) { show[i] = false; data[i].Data.shrink(0); data[i].Offset = 0; @@ -655,8 +662,8 @@ void ShowDemoWindow(bool* p_open) { if (ImGui::Button(paused ? "Resume" : "Pause", ImVec2(100,0))) paused = !paused; ImGui::Separator(); - for (int i = 0; i < 10; ++i) { - char label[8]; + for (int i = 0; i < K_CHANNELS; ++i) { + char label[K_CHANNELS]; sprintf(label, show[i] ? "data_%d*" : "data_%d", i); ImGui::Selectable(label, false, 0, ImVec2(100, 0)); if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { @@ -670,18 +677,16 @@ void ShowDemoWindow(bool* p_open) { static t_float t = 0; if (!paused) { t += ImGui::GetIO().DeltaTime; - for (int i = 0; i < 10; ++i) { + for (int i = 0; i < K_CHANNELS; ++i) { if (show[i]) - data[i].AddPoint(t, data[i].Data.empty() ? - 0.25f + 0.5f * (t_float)rand() / t_float(RAND_MAX) : - data[i].Data.back().y + (0.005f + 0.0002f * (t_float)rand() / t_float(RAND_MAX)) * (-1 + 2 * (t_float)rand() / t_float(RAND_MAX))); + data[i].AddPoint(t, (i+1)*0.1f + RandomRange(-0.01f,0.01f)); } } ImPlot::SetNextPlotLimitsX((double)t - 10, t, paused ? ImGuiCond_Once : ImGuiCond_Always); if (ImPlot::BeginPlot("##DND")) { - for (int i = 0; i < 10; ++i) { + for (int i = 0; i < K_CHANNELS; ++i) { if (show[i] && data[i].Data.size() > 0) { - char label[8]; + char label[K_CHANNELS]; sprintf(label, "data_%d", i); ImPlot::PlotLine(label, &data[i].Data[0].x, &data[i].Data[0].y, data[i].Data.size(), data[i].Offset, 2 * sizeof(t_float)); }