diff --git a/implot.cpp b/implot.cpp index 996b754..6bf565c 100644 --- a/implot.cpp +++ b/implot.cpp @@ -2486,11 +2486,11 @@ ImPlotLimits GetPlotQuery(int y_axis_in) { bool HorizontalGuide(const char* id, double* value, const ImVec4& col, float thickness) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "HorizontalGuide() needs to be called between BeginPlot() and EndPlot()!"); - const float grab_size = ImMax(6.0f, thickness); + const float grab_size = ImMax(5.0f, thickness); float xl = gp.BB_Plot.Min.x; float xr = gp.BB_Plot.Max.x; float y = IM_ROUND(PlotToPixels(0, *value).y); - const bool outside = (y < gp.BB_Plot.Min.y - grab_size / 2 || y > gp.BB_Plot.Max.y + grab_size / 2); + const bool outside = y < (gp.BB_Plot.Min.y - grab_size / 2) || y > (gp.BB_Plot.Max.y + grab_size / 2); if (outside) return false; @@ -2546,11 +2546,11 @@ bool HorizontalGuide(const char* id, double* value, const ImVec4& col, float thi bool VerticalGuide(const char* id, double* value, const ImVec4& col, float thickness) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "VerticalGuide() needs to be called between BeginPlot() and EndPlot()!"); - const float grab_size = ImMax(6.0f, thickness); + const float grab_size = ImMax(5.0f, thickness); float yt = gp.BB_Plot.Min.y; float yb = gp.BB_Plot.Max.y; float x = IM_ROUND(PlotToPixels(*value,0).x); - const bool outside = (x < gp.BB_Plot.Min.x - grab_size / 2 || x > gp.BB_Plot.Max.x + grab_size / 2); + const bool outside = x < (gp.BB_Plot.Min.x - grab_size / 2) || x > (gp.BB_Plot.Max.x + grab_size / 2); if (outside) return false; @@ -2596,6 +2596,55 @@ bool VerticalGuide(const char* id, double* value, const ImVec4& col, float thick return dragging; } +bool AnchorPoint(const char* id, double* x, double* y, const ImVec4& col, float radius) { + ImPlotContext& gp = *GImPlot; + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "AnchorPoint() needs to be called between BeginPlot() and EndPlot()!"); + const float grab_size = ImMax(5.0f, 2*radius); + const bool outside = !GetPlotLimits().Contains(*x,*y); + if (outside) + return false; + + ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col; + ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color); + ImDrawList& DrawList = *GetPlotDrawList(); + + ImVec2 pos = PlotToPixels(*x,*y); + + PushPlotClipRect(); + DrawList.AddCircleFilled(pos, radius, col32); + PopPlotClipRect(); + + int yax = GetCurrentYAxis(); + ImVec2 old_cursor_pos = ImGui::GetCursorScreenPos(); + ImVec2 new_cursor_pos = ImVec2(pos - ImVec2(grab_size,grab_size)*0.5f); + ImGui::SetItemAllowOverlap(); + ImGui::SetCursorScreenPos(new_cursor_pos); + ImGui::InvisibleButton(id, ImVec2(grab_size, grab_size)); + ImGui::SetCursorScreenPos(old_cursor_pos); + if (ImGui::IsItemHovered() || ImGui::IsItemActive()) { + ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); + // double range_x = gp.XTicks.Size > 1 ? (gp.XTicks.Ticks[1].PlotPos - gp.XTicks.Ticks[0].PlotPos) : gp.CurrentPlot->XAxis.Range.Size(); + // char buf[32]; + // snprintf(buf, 32, "%s = %.*f", id, Precision(range_x), *value); + // ImVec2 size = ImGui::CalcTextSize(buf); + // const int pad = 2; + // PushPlotClipRect(); + // DrawList.AddRectFilled(ImVec2(x - size.x/2 - pad, yb - size.y - 2*pad), ImVec2(x + pad + size.x/2, yb), col32); + // DrawList.AddText(ImVec2(x - size.x/2, yb - size.y - pad), CalcTextColor(color), buf); + // PopPlotClipRect(); + } + + bool dragging = false; + if (ImGui::IsItemActive() && ImGui::IsMouseDragging(0)) { + *x = ImPlot::GetPlotMousePos().x; + *y = ImPlot::GetPlotMousePos().y; + *x = ImClamp(*x, gp.X.Axis->Range.Min, gp.X.Axis->Range.Max); + *y = ImClamp(*y, gp.Y[yax].Axis->Range.Min, gp.Y[yax].Axis->Range.Max); + dragging = true; + } + return dragging; +} + bool IsLegendEntryHovered(const char* label_id) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "IsPlotItemHighlight() needs to be called between BeginPlot() and EndPlot()!"); diff --git a/implot.h b/implot.h index 95226b0..a3edd5e 100644 --- a/implot.h +++ b/implot.h @@ -453,11 +453,6 @@ IMPLOT_API ImPlotPoint GetPlotMousePos(int y_axis = IMPLOT_AUTO); // Returns the current plot axis range. A negative y_axis uses the current value of SetPlotYAxis (0 initially). IMPLOT_API ImPlotLimits GetPlotLimits(int y_axis = IMPLOT_AUTO); -// Returns true if the current plot is being queried. -IMPLOT_API bool IsPlotQueried(); -// Returns the current plot query bounds. -IMPLOT_API ImPlotLimits GetPlotQuery(int y_axis = IMPLOT_AUTO); - //----------------------------------------------------------------------------- // Plot Tools //----------------------------------------------------------------------------- @@ -466,6 +461,13 @@ IMPLOT_API ImPlotLimits GetPlotQuery(int y_axis = IMPLOT_AUTO); IMPLOT_API bool HorizontalGuide(const char* id, double* y_value, const ImVec4& col = IMPLOT_AUTO_COL, float thickness = 1); // Shows a draggable vertical guide line. #col defaults to ImGuiCol_Text. IMPLOT_API bool VerticalGuide(const char* id, double* x_value, const ImVec4& col = IMPLOT_AUTO_COL, float thickness = 1); +// Shows a draggable anchor point. #col defaults to ImGuiCol_Text. +IMPLOT_API bool AnchorPoint(const char* id, double* x, double* y, const ImVec4& col = IMPLOT_AUTO_COL, float radius = 5); + +// Returns true if the current plot is being queried. Query must be enabled with ImPlotFlags_Query. +IMPLOT_API bool IsPlotQueried(); +// Returns the current plot query bounds. Query must be enabled with ImPlotFlags_Query. +IMPLOT_API ImPlotLimits GetPlotQuery(int y_axis = IMPLOT_AUTO); //----------------------------------------------------------------------------- // Legend Utils and Tools diff --git a/implot_demo.cpp b/implot_demo.cpp index 0c54c2c..03b0887 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -805,7 +805,7 @@ void ShowDemoWindow(bool* p_open) { } } //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Guide Lines")) { + if (ImGui::CollapsingHeader("Guides and Anchors")) { ImGui::BulletText("Click and drag the horizontal and vertical guide lines."); static double x1 = 0.2; static double x2 = 0.8; @@ -829,6 +829,36 @@ void ShowDemoWindow(bool* p_open) { ImPlot::PlotLine("Why Not?", xs, ys, 1000); ImPlot::EndPlot(); } + ImGui::BulletText("Click and drag the anchor points."); + ImPlotAxisFlags flags = ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoTickMarks; + ImPlot::SetNextPlotLimits(0,1,0,1,ImGuiCond_Always); + if (ImPlot::BeginPlot("##Bezier",0,0,ImVec2(-1,0),ImPlotFlags_CanvasOnly|ImPlotFlags_NoChild,flags,flags)) { + static ImPlotPoint P[] = {ImPlotPoint(0,0), ImPlotPoint(0.2,0.4), ImPlotPoint(0.8,0.6), ImPlotPoint(1,1)}; + static ImPlotPoint B[100]; + for (int i = 0; i < 100; ++i) { + double t = i / 99.0; + double u = 1 - t; + double w1 = u*u*u; + double w2 = 3*u*u*t; + double w3 = 3*u*t*t; + double w4 = t*t*t; + B[i] = ImPlotPoint(w1*P[0].x + w2*P[1].x + w3*P[2].x + w4*P[3].x, w1*P[0].y + w2*P[1].y + w3*P[2].y + w4*P[3].y); + } + static ImVec4 gray = ImVec4(0.5f,0.5f,0.5f,1.0f); + ImPlot::SetNextFillStyle(ImVec4(0,1,0,0.25f)); + ImPlot::PlotShaded("##bez",&B[0].x, &B[0].y, 100, 0, 0, sizeof(ImPlotPoint)); + ImPlot::SetNextLineStyle(ImVec4(0,1,0,1), 2); + ImPlot::PlotLine("##bez",&B[0].x, &B[0].y, 100, 0, sizeof(ImPlotPoint)); + ImPlot::SetNextLineStyle(gray, 2); + ImPlot::PlotLine("##h1",&P[0].x, &P[0].y, 2, 0, sizeof(ImPlotPoint)); + ImPlot::SetNextLineStyle(gray, 2); + ImPlot::PlotLine("##h2",&P[2].x, &P[2].y, 2, 0, sizeof(ImPlotPoint)); + ImPlot::AnchorPoint("P0",&P[0].x,&P[0].y,gray); + ImPlot::AnchorPoint("P1",&P[1].x,&P[1].y,gray); + ImPlot::AnchorPoint("P2",&P[2].x,&P[2].y,gray); + ImPlot::AnchorPoint("P3",&P[3].x,&P[3].y,gray); + ImPlot::EndPlot(); + } } //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Drag and Drop")) {