ScenicFramework / scenic

Core Scenic library
Apache License 2.0
1.99k stars 137 forks source link

Polygon fill error. #345

Closed jimsynz closed 6 months ago

jimsynz commented 6 months ago

Checklist

Versions and Environment

Elixir:

# elixir -v
Erlang/OTP 26 [erts-14.2.5] [source] [64-bit] [smp:10:10] [ds:10:10:10] [async-threads:1] [jit]

Elixir 1.16.2 (compiled with Erlang/OTP 24)

Erlang:

# erl -v
Erlang/OTP 26 [erts-14.2.5] [source] [64-bit] [smp:10:10] [ds:10:10:10] [async-threads:1] [jit]

Eshell V14.2.5 (press Ctrl+G to abort, type help(). for help)

Scenic:

# mix deps | grep -A1 scenic
* scenic 0.11.2 (Hex package) (mix)
  locked at 0.11.2 (scenic) 6f846cfe
  ok
* scenic_driver_local 0.11.0 (https://github.com/ScenicFramework/scenic_driver_local.git) (mix)
  locked at 789d5fd

I'm using SCENIC_TARGET_LOCAL=glfw to build on macOS asn cairo-gtk doesn't compile for me.

OS:

# uname -a
Darwin Eeloo 23.4.0 Darwin Kernel Version 23.4.0: Fri Mar 15 00:10:42 PDT 2024; root:xnu-10063.101.17~1/RELEASE_ARM64_T6000 arm64

AKA macOS Sonoma 14.4.1 (23E224)

Steps to reproduce

Draw a "lightning bolt" closed path and ask it to be filled:

path(
  [
    :begin,
    {:move_to, 19, 3},
    {:line_to, 14, 11},
    {:line_to, 17, 11},
    {:line_to, 17, 17},
    {:line_to, 22, 9},
    {:line_to, 19, 9},
    :close_path
  ],
  stroke: {1, {:color_rgba, {0x0, 0, 0, 0x7F}}},
  fill: {:color_rgba, {0xFF, 0xFF, 0xFF, 0xAF}},
  id: :charging
)
Screenshot 2024-05-14 at 10 12 57 AM

Expected Behavior

I expected the fill to be applied inside the path.

Actual Behavior

Fill was applied outside the closed path.

Stack Trace

N/A

Additional Comments

So, I was trying to create a tiny battery status widget and I wanted to place a "lightning bolt" over the battery icon when connected to an external power supply. I discovered that the small polygon was not being filled correctly:

Screenshot 2024-05-14 at 10 12 57 AM

Thinking that it was simply an artifact of the path being so small, I tried scaling it up to see if that fixed the problem:

path(
  [
    :begin,
    {:move_to, 55, 32},
    {:line_to, 0, 120},
    {:line_to, 33, 122},
    {:line_to, 33, 185},
    {:line_to, 88, 98},
    {:line_to, 55, 98},
    :close_path
  ],
  fill: :white,
  stroke: {1, :black}
)

However as you can see, the bigger version also has the same problem:

Screenshot 2024-05-14 at 10 44 58 AM

Then I thought it could be a problem caused by the horizontal and vertical lines somehow, but that didn't fix it either:

path(
  [
    :begin,
    {:move_to, 55, 32},
    {:line_to, 0, 120},
    {:line_to, 33, 122},
    {:line_to, 35, 185},
    {:line_to, 88, 98},
    {:line_to, 53, 96},
    :close_path
  ],
  fill: :white,
  stroke: {1, :black}
)

I can't see anything wrong with the way that I am using the path primitive, so I am pretty sure that this is a bug and not PEBKAC.

Screenshot 2024-05-14 at 10 49 05 AM

I did actually try other shapes like diamonds, hexagons etc, and had no problems so unfortunately I have no clues to help figure this out.

jimsynz commented 6 months ago

Okay, so I have found some more interesting information.

I have found that this happens for a square with a slightly pushed in side:

path(
  [
    :begin,
    {:move_to, 40, 40},
    {:line_to, 200, 40},
    {:line_to, 180, 120},
    {:line_to, 200, 200},
    {:line_to, 40, 200},
    :close_path
  ],
  fill: :white,
  stroke: {1, :red},
  id: :shape
)
Screenshot 2024-05-14 at 12 01 39 PM

But here's the kicker - only when we transition into the new scene from another scene.

My app has a StartupScene that performs some animations and then calls

  def handle_info({:animate_done, :background, style}, scene) do
    :timer.apply_after(1000, Scenic.ViewPort, :set_root, [
      scene.viewport,
      Podbox.Scenic.MainScene,
      [bg_style: style]
    ])

    {:noreply, scene}
  end

which transitions to the MainScene module. When the app starts up directly into the MainScene it renders correctly:

https://github.com/ScenicFramework/scenic/assets/59449/59a78782-7db0-456f-8042-998cc54f5ec7

But when the scene transitions it renders incorrectly. I also found that if I crash the scene after the transition it redraws correctly:

https://github.com/ScenicFramework/scenic/assets/59449/9cd883cc-30b2-4159-9f2d-9bcd3c094778

I've tried different variations of calling Scenic.ViewPort.set_root/3 - calling it from another process via task, using Process.send_after/3 and calling it more conventionally. Nothing seems to make a difference.

crertel commented 6 months ago

Okay, that's pretty wild!

I feel like that might be a bug in the scenic driver--have you tried it with both cairo and nvg?

jimsynz commented 6 months ago

yeah okay, so I just reinstalled cairo and gtk+3 and it compiled, so who knows why it was failing before. I can report that the problem doesn't manifest using cairo-gtk as the driver.

crertel commented 6 months ago

It still repros with glfw though right?

(And thank you very much for digging into this!)

jimsynz commented 6 months ago

Sorry - yes, it does. But since glfw is deprecated maybe we just shrug, close this issue and move on with our lives?

crertel commented 6 months ago

That seems reasonable to me. Great work @jimsynz !