ImDrawList: added AddBezier(), PathBezierTo() for cubic bezier curves (#311)

This commit is contained in:
ocornut 2015-08-28 13:18:34 +01:00
parent e58f99179a
commit 80ad773c87
4 changed files with 65 additions and 5 deletions

View File

@ -620,6 +620,7 @@ ImGuiStyle::ImGuiStyle()
DisplaySafeAreaPadding = ImVec2(4,4); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. DisplaySafeAreaPadding = ImVec2(4,4); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows.
AntiAliasedLines = true; // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU. AntiAliasedLines = true; // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU.
AntiAliasedShapes = true; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.) AntiAliasedShapes = true; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
CurveTessellationTol = 1.25f; // Tessellation tolerance. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.
Colors[ImGuiCol_Text] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); Colors[ImGuiCol_Text] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);
Colors[ImGuiCol_TextDisabled] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f); Colors[ImGuiCol_TextDisabled] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);
@ -1790,6 +1791,7 @@ void ImGui::NewFrame()
IM_ASSERT(g.IO.RenderDrawListsFn != NULL); // Must be implemented IM_ASSERT(g.IO.RenderDrawListsFn != NULL); // Must be implemented
IM_ASSERT(g.IO.Fonts->Fonts.Size > 0); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ? IM_ASSERT(g.IO.Fonts->Fonts.Size > 0); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ? IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
IM_ASSERT(g.Style.CurveTessellationTol > 0.0f); // Invalid
if (!g.Initialized) if (!g.Initialized)
{ {

12
imgui.h
View File

@ -647,6 +647,7 @@ struct ImGuiStyle
ImVec2 DisplaySafeAreaPadding; // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. ImVec2 DisplaySafeAreaPadding; // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows.
bool AntiAliasedLines; // Enable anti-aliasing on lines/borders. Disable if you are really tight on CPU/GPU. bool AntiAliasedLines; // Enable anti-aliasing on lines/borders. Disable if you are really tight on CPU/GPU.
bool AntiAliasedShapes; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.) bool AntiAliasedShapes; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
float CurveTessellationTol; // Tessellation tolerance. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.
ImVec4 Colors[ImGuiCol_COUNT]; ImVec4 Colors[ImGuiCol_COUNT];
IMGUI_API ImGuiStyle(); IMGUI_API ImGuiStyle();
@ -1100,15 +1101,18 @@ struct ImDrawList
IMGUI_API void AddImage(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& uv0, const ImVec2& uv1, ImU32 col = 0xFFFFFFFF); IMGUI_API void AddImage(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& uv0, const ImVec2& uv1, ImU32 col = 0xFFFFFFFF);
IMGUI_API void AddPolyline(const ImVec2* points, const int num_points, ImU32 col, bool closed, float thickness, bool anti_aliased); IMGUI_API void AddPolyline(const ImVec2* points, const int num_points, ImU32 col, bool closed, float thickness, bool anti_aliased);
IMGUI_API void AddConvexPolyFilled(const ImVec2* points, const int num_points, ImU32 col, bool anti_aliased); IMGUI_API void AddConvexPolyFilled(const ImVec2* points, const int num_points, ImU32 col, bool anti_aliased);
inline void AddBezier(const ImVec2& pos0, const ImVec2& cp0, const ImVec2& cp1, const ImVec2& pos1, ImU32 col, float thickness, int num_segments = 0) { PathBezierTo(pos0, cp0, cp1, pos1, num_segments); PathStroke(col, false, thickness); }
// Stateful path API, add points then finish with PathFill() or PathStroke() // Stateful path API, add points then finish with PathFill() or PathStroke()
inline void PathClear() { _Path.resize(0); } inline void PathClear() { _Path.resize(0); }
inline void PathLineTo(const ImVec2& p) { _Path.push_back(p); } inline void PathLineTo(const ImVec2& pos) { _Path.push_back(pos); }
IMGUI_API void PathArcTo(const ImVec2& centre, float radius, float a_min, float a_max, int num_segments = 10); inline void PathLineToMergeDuplicate(const ImVec2& pos) { if (_Path.Size == 0 || _Path[_Path.Size-1].x != pos.x || _Path[_Path.Size-1].y != pos.y) _Path.push_back(pos); }
IMGUI_API void PathArcToFast(const ImVec2& centre, float radius, int a_min_of_12, int a_max_of_12);
IMGUI_API void PathRect(const ImVec2& a, const ImVec2& b, float rounding = 0.0f, int rounding_corners = 0x0F);
inline void PathFill(ImU32 col) { AddConvexPolyFilled(_Path.Data, _Path.Size, col, true); PathClear(); } inline void PathFill(ImU32 col) { AddConvexPolyFilled(_Path.Data, _Path.Size, col, true); PathClear(); }
inline void PathStroke(ImU32 col, bool closed, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, closed, thickness, true); PathClear(); } inline void PathStroke(ImU32 col, bool closed, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, closed, thickness, true); PathClear(); }
IMGUI_API void PathArcTo(const ImVec2& centre, float radius, float a_min, float a_max, int num_segments = 10);
IMGUI_API void PathArcToFast(const ImVec2& centre, float radius, int a_min_of_12, int a_max_of_12); // Use precomputed angles for a 12 steps circle
IMGUI_API void PathBezierTo(const ImVec2& p0, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, int num_segments = 0);
IMGUI_API void PathRect(const ImVec2& rect_min, const ImVec2& rect_max, float rounding = 0.0f, int rounding_corners = 0x0F);
// Channels // Channels
// - Use to simulate layers. By switching channels to can render out-of-order (e.g. submit foreground primitives before background primitives) // - Use to simulate layers. By switching channels to can render out-of-order (e.g. submit foreground primitives before background primitives)

View File

@ -1392,6 +1392,10 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
{ {
ImGui::Checkbox("Anti-aliased lines", &style.AntiAliasedLines); ImGui::Checkbox("Anti-aliased lines", &style.AntiAliasedLines);
ImGui::Checkbox("Anti-aliased shapes", &style.AntiAliasedShapes); ImGui::Checkbox("Anti-aliased shapes", &style.AntiAliasedShapes);
ImGui::PushItemWidth(100);
ImGui::DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, FLT_MAX, NULL, 2.0f);
if (style.CurveTessellationTol < 0.0f) style.CurveTessellationTol = 0.10f;
ImGui::PopItemWidth();
ImGui::TreePop(); ImGui::TreePop();
} }

View File

@ -639,6 +639,56 @@ void ImDrawList::PathArcTo(const ImVec2& centre, float radius, float amin, float
} }
} }
static void PathBezierToCasteljau(ImVector<ImVec2>* path, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float tess_tol, int level)
{
float dx = x4 - x1;
float dy = y4 - y1;
float d2 = ((x2 - x4) * dy - (y2 - y4) * dx);
float d3 = ((x3 - x4) * dy - (y3 - y4) * dx);
d2 = (d2 >= 0) ? d2 : -d2;
d3 = (d3 >= 0) ? d3 : -d3;
if ((d2+d3) * (d2+d3) < tess_tol * (dx*dx + dy*dy))
{
path->push_back(ImVec2(x4, y4));
}
else if (level < 10)
{
float x12 = (x1+x2)*0.5f, y12 = (y1+y2)*0.5f;
float x23 = (x2+x3)*0.5f, y23 = (y2+y3)*0.5f;
float x34 = (x3+x4)*0.5f, y34 = (y3+y4)*0.5f;
float x123 = (x12+x23)*0.5f, y123 = (y12+y23)*0.5f;
float x234 = (x23+x34)*0.5f, y234 = (y23+y34)*0.5f;
float x1234 = (x123+x234)*0.5f, y1234 = (y123+y234)*0.5f;
PathBezierToCasteljau(path, x1,y1, x12,y12, x123,y123, x1234,y1234, tess_tol, level+1);
PathBezierToCasteljau(path, x1234,y1234, x234,y234, x34,y34, x4,y4, tess_tol, level+1);
}
}
void ImDrawList::PathBezierTo(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments)
{
PathLineToMergeDuplicate(p1);
if (num_segments == 0)
{
// Auto-tessellated
PathBezierToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, GImGui->Style.CurveTessellationTol, 0);
}
else
{
float t_step = 1.0f / (float)num_segments;
for (int i_step = 1; i_step <= num_segments; i_step++)
{
float t = t_step * i_step;
float u = 1.0f - t;
float w1 = u*u*u;
float w2 = 3*u*u*t;
float w3 = 3*u*t*t;
float w4 = t*t*t;
_Path.push_back(ImVec2(w1*p1.x + w2*p2.x + w3*p3.x + w4*p4.x, w1*p1.y + w2*p2.y + w3*p3.y + w4*p4.y));
}
}
}
void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, int rounding_corners) void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, int rounding_corners)
{ {
float r = rounding; float r = rounding;