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

strokeBounds wrong when applyMatrix=false #2065

Open
mokafolio opened this issue Feb 12, 2024 · 5 comments
Open

strokeBounds wrong when applyMatrix=false #2065

mokafolio opened this issue Feb 12, 2024 · 5 comments

Comments

@mokafolio
Copy link

strokeBounds are often wrong in a bunch of basic configurations when applyMatrix is set to false. This mainly appears to affect round and miter bounds, but I was also able to create wrong results with bevel.

Link to reproduction test-case

link

After years of not having looked at the paper source code, I am pretty convinced, that the current implementation of getStrokeBounds can't work in a lot of cases when applyMatrix is set to false because of the way matrix and strokeMatrix are separated. i.e for the bevel and miter case, you really want to calculate the stroke geometry in local space and simply apply the paths matrix last to get consistently correct results that match the canvas stroke geometry.

Hope I didn't miss anything :)

@mokafolio
Copy link
Author

mokafolio commented Feb 12, 2024

For round joins, i similarly believe that the current ellipse based approach won't work consistently when applyMatrix is false. I think a viable approach could be the following:

  1. Compute bevel joins in local path space (ba and bb)
  2. the current join position bc is the center of the arc of the round join going through those points.
  3. Calculate the normalized perpendicular vector d to ba - bb
  4. Compute the round join tip bc: i + d * stroke_radius
  5. Transform ba, bb and bc with the paths matrix and min max it with the current bounds being worked on.

I am fairly certain that should do the trick but I have not tested it yet and there might be a simpler way.

@mokafolio
Copy link
Author

I tested the approach from my previous comment and while its a better approximation, it does not work all the time. Two more ideas:

create a bezier curve approximation of the round join arc in local space. Apply the matrix to the bezier positions/handles and compute the bounds of the curve to merge with the overall bounds (and of course do this for every joint).

Or:

Do it the most simple way of simply sampling the arc of the round join in multiple positions. Multiply all sampled positions by the matrix and min max them respectively.

The first approach might be a bit more expensive but should nicely work with all the existing code in paper as we only need to create a temporary curve for each join.

The second approach is dead simple and relatively cheap but its hard to pick a sample count that works well with all kinds of different scaling factors/matrices.

@mokafolio
Copy link
Author

I just tested the first approach from my previous comment, essentially estimating the round join arc with curves and that appears to work consistently. To summarize for round joins:

  • compute bevel join vertices in local space (a and b)
  • compute round join tip (c) based on bevel join positions and tangents (as described in my first comment)
  • create a tmp path that arcs from a to b through c.
  • apply matrix to tmp path and merge resulting bounds.

@lehni
Copy link
Member

lehni commented Aug 31, 2024

Hey @mokafolio, long time! Thank you for your analysis and input. Would you be interested in creating a PR to fix this? I have stopped working on Paper.js for the time being, mainly due to lack of time and also because it's increasingly hard to work on this aging JS code-base. I would love to find the time modernise it all and convert it to ESM. Would you be interested on working on this with me?

@mokafolio
Copy link
Author

Hey @lehni, I had only found/explored and fixed this in some of my c++ code (and was cross referencing the paper implementation along the way). I don't think I have the time to back port it right now. Unfortunately the same goes for a full paper rewrite. As much as I love the idea, I don't think I can make the time right now between work and all the other life stuff going on :(

Hope everything is good!

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

No branches or pull requests

2 participants