diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 9a7e68f27c54..d4aaf5a2114e 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -187,6 +187,55 @@ static void HelpMarker(const char* desc) } } +// Helper to interact with a control point +// mouse left click/move for move the control point +static ImVec2 HelpManipulateControlPoint(const int point_index, ImVec2& point_coords, const float point_radius, const ImVec4 canvas) +{ + static int selected_control_point = -1; + + // current point control + ImVec2 point = ImVec2(canvas.x + point_coords.x * canvas.z, canvas.y + point_coords.y * canvas.w); + + bool hovered = false; + if (ImGui::IsItemHovered()) + { + if (ImGui::IsMouseHoveringRect( + ImVec2(point.x - point_radius * 0.5f, point.y - point_radius * 0.5f), + ImVec2(point.x + point_radius * 0.5f, point.y + point_radius * 0.5f))) + { + hovered = true; + + // select if active and no point was selected before + // for have only one point movable at same time + if (selected_control_point < 0 && ImGui::IsItemActive()) + selected_control_point = point_index; + } + } + + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + // draw point background color + draw_list->AddCircleFilled(point, point_radius, + (selected_control_point == point_index) ? IM_COL32(0, 0, 255, 255) : + (hovered) ? IM_COL32(0, 127, 127, 255) : IM_COL32(255, 0, 0, 255)); + // draw point edge + draw_list->AddCircle(point, point_radius, IM_COL32(255, 255, 255, 255), 0, 2.0f); + + // unselect point + if (ImGui::IsMouseReleased(ImGuiMouseButton_Left)) + selected_control_point = -1; + + // move point, will be updated on next frame + if (selected_control_point == point_index && + ImGui::IsMouseDragging(ImGuiMouseButton_Left, 0.0f)) + { + ImGuiIO& io = ImGui::GetIO(); + point_coords.x += io.MouseDelta.x / (canvas.z + 1e-5f); + point_coords.y += io.MouseDelta.y / (canvas.w + 1e-5f); + } + + return point; +} + // Helper to display basic user controls. void ImGui::ShowUserGuide() { @@ -7106,6 +7155,79 @@ static void ShowExampleAppCustomRendering(bool* p_open) ImGui::EndTabItem(); } + if (ImGui::BeginTabItem("Curves")) + { + static ImVec2 bezier_control_points[4] = { ImVec2(0.12, 0.82), ImVec2(0.34, 0.21), ImVec2(0.90, 0.41), ImVec2(0.56, 0.82) }; // ratio of canvas size + static float control_point_radius = 10.0f; + static int curve_type = 0; // 0 quadratic, 1 cubic + static bool curve_segments_override = false; + static int curve_segments_override_v = 8; + + ImGui::RadioButton("Quad Bezier", &curve_type, 0); + ImGui::SameLine(); HelpMarker("a Quad bezier have 3 control points;"); + ImGui::SameLine(); ImGui::RadioButton("Cubic Bezier", &curve_type, 1); + ImGui::SameLine(); HelpMarker("a Cubic bezier have 4 control points;"); + ImGui::Checkbox("##curvessegmentoverride", &curve_segments_override); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + if (ImGui::SliderInt("Curves segments", &curve_segments_override_v, 3, 40)) + curve_segments_override = true; + + ImGui::Text("Mouse Left : move control points"); + + // canvas from last tab + // Using InvisibleButton() as a convenience 1) it will advance the layout cursor and 2) allows us to use IsItemHovered()/IsItemActive() + ImVec2 canvas_p0 = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates! + ImVec2 canvas_sz = ImGui::GetContentRegionAvail(); // Resize canvas to what's available + if (canvas_sz.x < 50.0f) canvas_sz.x = 50.0f; + if (canvas_sz.y < 50.0f) canvas_sz.y = 50.0f; + ImVec2 canvas_p1 = ImVec2(canvas_p0.x + canvas_sz.x, canvas_p0.y + canvas_sz.y); + + // Draw border and background color + ImGuiIO& io = ImGui::GetIO(); + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + draw_list->AddRectFilled(canvas_p0, canvas_p1, IM_COL32(50, 50, 50, 255)); + draw_list->AddRect(canvas_p0, canvas_p1, IM_COL32(255, 255, 255, 255)); + + // This will catch our interactions, only left button, no scrolling on this one + ImGui::InvisibleButton("canvas", canvas_sz, ImGuiButtonFlags_MouseButtonLeft); + ImVec4 canvas_rc = ImVec4(canvas_p0.x, canvas_p0.y, canvas_p1.x - canvas_p0.x, canvas_p1.y - canvas_p0.y); + + draw_list->PushClipRect(canvas_p0, canvas_p1, true); // canvas clipping + { + draw_list->ChannelsSplit(2); // split channels + + // calc and render control point in top layer + draw_list->ChannelsSetCurrent(1); + ImVec2 cp[4]; // control point + for (int i = 0; i < 3 + curve_type; i++) // 3 when curve is quadratic, 4 when curve is cubic + { + cp[i] = HelpManipulateControlPoint(i, bezier_control_points[i], control_point_radius, canvas_rc); + } + + // render curve and edges in bottom layer + draw_list->ChannelsSetCurrent(0); + const ImU32 curve_color = IM_COL32(255, 255, 0, 255); + const int curve_segments = curve_segments_override ? curve_segments_override_v : 0; + if (curve_type) // bezier cubic + { + draw_list->AddBezierCubic(cp[0], cp[1], cp[2], cp[3], curve_color, 2.0f, curve_segments); // curve + draw_list->AddLine(cp[0], cp[1], IM_COL32(255, 255, 255, 255), 1.5f); // edge from control point 0 to 1 + draw_list->AddLine(cp[2], cp[3], IM_COL32(255, 255, 255, 255), 1.5f); // edge from control point 2 to 3 + } + else // bezier quadratic + { + draw_list->AddBezierQuadratic(cp[0], cp[1], cp[2], curve_color, 2.0f, curve_segments); // curve + draw_list->AddLine(cp[0], cp[1], IM_COL32(255, 255, 255, 255), 1.5f); // edge from control point 0 to 1 + draw_list->AddLine(cp[1], cp[2], IM_COL32(255, 255, 255, 255), 1.5f); // edge from control point 2 to 3 + } + + draw_list->ChannelsMerge(); // merge channels + } + draw_list->PopClipRect(); + + ImGui::EndTabItem(); + } + ImGui::EndTabBar(); }