-
-
Notifications
You must be signed in to change notification settings - Fork 11.1k
Description
Version/Branch of Dear ImGui:
Version: v1.88
Branch: master
Back-end/Renderer/Compiler/OS
Back-ends: any
Compiler: any
Operating System: windows
My Issue/Question:
Currently trying to build out some utility functions to render svgs. What I found is that when using:
svg_draw_list.PathBezierCubicCurveTo(
ImVec2{ x1, y1 },
ImVec2{ x2, y2 },
ImVec2{ current_x + cmd->x, current_y + cmd->y });
There's a defaulted num_segment value of 0 which eventually uses the functionality from #3127
static void PathBezierCubicCurveToCasteljau(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;
PathBezierCubicCurveToCasteljau(path, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1);
PathBezierCubicCurveToCasteljau(path, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1);
}
}
static void PathBezierQuadraticCurveToCasteljau(ImVector<ImVec2>* path, float x1, float y1, float x2, float y2, float x3, float y3, float tess_tol, int level)
{
float dx = x3 - x1, dy = y3 - y1;
float det = (x2 - x3) * dy - (y2 - y3) * dx;
if (det * det * 4.0f < tess_tol * (dx * dx + dy * dy))
{
path->push_back(ImVec2(x3, y3));
}
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 x123 = (x12 + x23) * 0.5f, y123 = (y12 + y23) * 0.5f;
PathBezierQuadraticCurveToCasteljau(path, x1, y1, x12, y12, x123, y123, tess_tol, level + 1);
PathBezierQuadraticCurveToCasteljau(path, x123, y123, x23, y23, x3, y3, tess_tol, level + 1);
}
}
Quick eyeballing of the code and you'll notice that with a tessellation tolerance <= 0.0f no points will ever be added, this includes the start and end point of the curve (presumably the start of the curve has already been added). I'm not sure if this is intended, I'd assume a failover of at least a line drawn to the end point? I ran into this by accident because I didn't specifically set the tessellation value and apparently it was zeroed out. In my case it clearly broke drawings with a start point followed by a curve because the resulting path only contained the start point, which of course doesn't end up rendering much of anything to notice visually.
Screenshots/Video
For reference...my svg renderer is not complete and is still buggy, but here's what I was dealing with:
The original svg was a chess piece, in case that isn't clear.
Standalone, minimal, complete and verifiable example: (see #2261)
ImGui::Begin("Example Bug");
ImDrawList* current_list = ImGui::GetWindowDrawList();
ImGuiWindow* current_window = ImGui::GetCurrentWindow();
ImDrawListSharedData* shared_data = ImGui::GetDrawListSharedData();
shared_data->CurveTessellationTol = 0.0f; //if this isn't modified it could easily be 0'd out memory which does this
ImDrawList svg_draw_list = ImDrawList(shared_data);
float x1 = 0.0f + current_window->DC.CursorPos.x;
float x2 = 100.f + current_window->DC.CursorPos.x;
float x3 = 200.0f + current_window->DC.CursorPos.x;
float y1 = 0.0f + current_window->DC.CursorPos.y;
float y2 = 100.0f + current_window->DC.CursorPos.y;
float y3 = 200.0f + current_window->DC.CursorPos.y;
svg_draw_list.PathLineTo(ImVec2{ x1, y1 });
svg_draw_list.PathBezierCubicCurveTo(
ImVec2{ x2, y1 },
ImVec2{ x3, y2 },
ImVec2{ x3, y3 }); // should now how an arc like path
//svg_draw_list.AddPolyline(svg_draw_list._Path.Data, svg_draw_list._Path.Size, ImU32{0xffffffff}, 0, 1.0f);
//svg_draw_list.AddConvexPolyFilled(svg_draw_list.Path.Data, svg_draw_list._Path.Size, ImU32{0xffffffff});
current_list->AddPolyline(svg_draw_list._Path.Data, svg_draw_list._Path.Size, ImU32{ 0xffffffff }, 0, 10.0f); // as a white line
//svg_draw_list.PathClear();
// should see this:
shared_data->CurveTessellationTol = 0.1f;
ImDrawList nsvg_draw_list = ImDrawList(shared_data);
x1 += 200.0f;
x2 += 200.0f;
x3 += 200.0f;
nsvg_draw_list.PathLineTo(ImVec2{ x1, y1 });
nsvg_draw_list.PathBezierCubicCurveTo(
ImVec2{ x2, y1 },
ImVec2{ x3, y2 },
ImVec2{ x3, y3 });
current_list->AddPolyline(nsvg_draw_list._Path.Data, nsvg_draw_list._Path.Size, ImU32{ 0xff0000ff }, 0, 10.0f); // as a red line
//nsvg_draw_list.PathClear();
current_list->AddRect(ImVec2{ 1000.0f, 0.0f }, ImVec2{ 1100.0f, 100.0f }, ImU32{0xffffffff}, 0.0f);
//note: svg_draw_list and nsvg_draw_list have different _Path data!
ImGui::End();