Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Polyline with sharp angles causes segments to nearly disappear #3366

Closed
tlbtlbtlb opened this issue Jul 28, 2020 · 5 comments
Closed

Polyline with sharp angles causes segments to nearly disappear #3366

tlbtlbtlb opened this issue Jul 28, 2020 · 5 comments

Comments

@tlbtlbtlb
Copy link

Dear ImGui 1.78 WIP (17703)
--------------------------------
sizeof(size_t): 8, sizeof(ImDrawIdx): 4, sizeof(ImDrawVert): 20
define: __cplusplus=201703
define: __APPLE__
define: __GNUC__=4
define: __clang_version__=11.0.3 (clang-1103.0.32.62)
--------------------------------
io.BackendPlatformName: imgui_impl_sdl
io.BackendRendererName: imgui_impl_opengl3
io.ConfigFlags: 0x00000000
io.ConfigMacOSXBehaviors
io.ConfigInputTextCursorBlink
io.ConfigWindowsResizeFromEdges
io.ConfigWindowsMemoryCompactTimer = 60.0f
io.BackendFlags: 0x0000000E
 HasMouseCursors
 HasSetMousePos
 RendererHasVtxOffset
--------------------------------
io.Fonts: 5 fonts, Flags: 0x00000000, TexSize: 2048,4096
io.DisplaySize: 1792.00,1042.00
io.DisplayFramebufferScale: 2.00,2.00
--------------------------------
style.WindowPadding: 8.00,8.00
style.WindowBorderSize: 1.00
style.FramePadding: 4.00,3.00
style.FrameRounding: 0.00
style.FrameBorderSize: 0.00
style.ItemSpacing: 8.00,4.00
style.ItemInnerSpacing: 4.00,4.00

Version/Branch of Dear ImGui:

Version: 1.78 WIP
Branch: master

Back-end/Renderer/Compiler/OS

Back-ends: imgui_impl_sdl.cpp + imgui_impl_opengl3.cpp (or specify if using a custom engine/back-end)
Compiler: clang 9
Operating System: OSX

My Issue/Question:

Calling AddPolyline with a line with sharp corners causes the line segments adjacent to the corners to be nearly invisible. See the first screenshot below of graph drawn with

  GetWindowDrawList()->AddPolyline(&path[0], path.size(), IM_COL32(0xff, 0x5b, 0xfc, 0xff), false, 2.0f);

The culprit seems to be the algorithm for IM_FIXNORMAL2F as called in ImDrawList::AddPolyline. I'm in the case with use_texture=true, thick_line=true, closed=false, fractional_thickness=0.0

When there is a sharp corner, averaging 2 successive normals produces results with magnitude much less than 1. IM_FIXNORMAL2F doesn't scale the normal up by more than a factor of 2, so the end result is a vanishingly thin line.

Screenshots/Video

The top squiggle shows the current behavior. Right now, I'm working around it by adding some duplicate points when there's a sharp corner, as shown in the bottom squiggle.

This comes up for real a lot in my application which involves graphing time series which are often jagged.

image

Standalone, minimal, complete and verifiable example: (see #2261)

// Here's some code anyone can copy and paste to reproduce your issue
      ImGui::Begin("Example Bug");
      float x=0, y=0;
      if (1) {
        // The version I want to work
        vector<ImVec2> path;
        path.emplace_back(x+0, y+100);
        path.emplace_back(x+98, y+100);
        path.emplace_back(x+99, y+110);
        path.emplace_back(x+100, y+50);
        path.emplace_back(x+110, y+100);
        path.emplace_back(x+120, y+100);
        path.emplace_back(x+200, y+100);
        GetWindowDrawList()->AddPolyline(&path[0], path.size(), IM_COL32(0xff, 0x5b, 0xfc, 0xff), false, 2.0f);
        y += 200;
      }
      if (1) {
        // The version with dummy points inserted
        vector<ImVec2> path;
        path.emplace_back(x+0, y+100);
        path.emplace_back(x+98, y+100);
        path.emplace_back(x+99, y+110);
        path.emplace_back(x+99, y+110);
        path.emplace_back(x+100, y+50);
        path.emplace_back(x+100, y+50);
        path.emplace_back(x+110, y+100);
        path.emplace_back(x+120, y+100);
        path.emplace_back(x+200, y+100);
        GetWindowDrawList()->AddPolyline(&path[0], path.size(), IM_COL32(0xff, 0x5b, 0xfc, 0xff), false, 2.0f);
        y += 200;
      }  
      ImGui::End();
@tlbtlbtlb
Copy link
Author

I'm currently pre-processing my graphs with the code below, which produces great results. It groups the points into clusters that don't have acute angles (by checking the sign of the dot product of successive line segments).

  // pts is a vector<ImVec2> of points in a time series graph
  size_t i = 0;
  while (i + 1 < pts.size()) {

    int nlin = 2;
    while (i + nlin < pts.size()) {
      auto [x0, y0] = pts[i+nlin-2];
      auto [x1, y1] = pts[i+nlin-1];
      auto [x2, y2] = pts[i+nlin];
      auto s0x = x1-x0, s0y = y1-y0; 
      auto s1x = x2-x1, s1y = y2-y1;
      auto dotprod = s1x*s0x + s1y*s0y;
      if (dotprod < 0) break;
      nlin ++;
    }

    dl->AddPolyline(&pts[i], nlin, color, false, width);
    i += nlin-1;
  }

A typical small graph this produces is:

image

@ocornut
Copy link
Owner

ocornut commented Jul 30, 2020

Hello Trevor,

Thanks for your report!

We had this issue in mind a few months ago, there is (was-) actually a perfectly formed PR in #2964 back in January.
It was only trumped by the fact we worked on a conflicting feature/optimization (#3245 now merged) which means #2964 needs to be reworked accordingly. I sort of feel we should have adopted #2964 earlier, and we could perhaps reformulate the optimization in #3245 so it's only used for specialized or convex shapes (e.g. rounded rectangles, which are a good portion of what we render).

Give us a little time and we'll resurrect #2964 to fix this :)

Two things:

  • Note that 1.0f thick line should not be affected by the issue.
  • I wonder if temporarily clearing the ImDrawListFlags_AntiAliasedLinesUseTex flag in your draw list to use the old path would make any difference? You can use:
    draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLinesUseTex;
    To clear the flag, and
    draw_list->Flags |= ImDrawListFlags_AntiAliasedLinesUseTex;
    To set it back.
    I'd be interested in hearing the feedback for that!

-Omar

@tlbtlbtlb
Copy link
Author

There's hardly any difference with texture or not. Here's without texture:

image

and with:

image

I only notice that the downward sloping part of the line looks slightly thicker with texture.

Turning off antialiasing entirely (draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines) avoids the missing parts, but it's blocky:

image

thickness=1.0f looks pretty wimpy on a Retina display.

At any rate, my workaround is to split into multiple calls to AddPolyline when there's a sharp angle: https://gitlab.com/umbrellaresearch/yoga2/-/blob/tlbdev/studio/ysgui.cc#L369

@ocornut
Copy link
Owner

ocornut commented Apr 19, 2021

See this committed improvement:
#4053 (comment)

Toggling on/off:

3366

This is obviously not perfect but better. We actually can easily preserve thickness by raising the IM_FIXNORMAL2F_MAX_INVLEN2` threshold:

image

But the extrusion of geometry to preserve thickness means that geometry can stray very far out, which for this specific use of reading a plot is a really bad thing (before of this case I reverted my initial change of increasing that threshold, and I think once we have bevel option in you'll be able to choose depending on use).

I'll close this even though it is only improved not fully fixed. When we make progress on #2964 an update will be posted here too.

@ocornut ocornut closed this as completed Apr 19, 2021
@ocornut
Copy link
Owner

ocornut commented Apr 19, 2021

Interestingly the path not using textures looks marginally better in this case:
tex_no_tex

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants