meerk40t / svgelements

SVG Parsing for Elements, Paths, and other SVG Objects.
MIT License
124 stars 28 forks source link

Cubic Bezier incorrect bounding box #214

Closed morpho-matters closed 1 year ago

morpho-matters commented 1 year ago

This may be related to #186, but thought I'd bring it up in case it isn't known about. The computed bounding box of certain cubic beziers seems to be incorrect sometimes. Here's an example:

from svgelements import CubicBezier

cubic = CubicBezier(0, -2-3j, -1-4j, -3j)
bbox = cubic.bbox()
print(bbox)  # outputs (-1.1547005383792515, -3.0, 0.0, 0.0)

This outputs (-1.1547005383792515, -3.0, 0.0, 0.0), but ymin should really be lower than -3. Here is a picture of the bezier with the incorrectly computed bounding box: bad-bbox

svgelements version: 1.9.0 Python version: 3.8.1 OS: Windows

tatarize commented 1 year ago
<svg width="240" height="320" viewBox="-5 -5 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M 0,0 C -2,-3 -1,-4 0,-3" stroke="#FFC681" stroke-width="0.05px"/>
  <rect x="0" y="0" width="1.1547005383792515" height="3.0" transform="scale(-1,-1)" stroke="#FFC681" stroke-width="0.05px"/>
</svg>
cubic

Confirmed. That bit seems quite clearly not correctly the bounding box.

tatarize commented 1 year ago

This is an edge case when p1 - (3 * p2) + (3 * p3) - p4 == 0 In this case for y our values are 0, -3, -4, -3. Which gives us 0 - (3 * -3) + (3 * -4) - -3 which is 0 + 9 - 12 + 3 which does equal 0. In this case we have a division-by-zero numerical instability which we avoided providing the extrema, but here the extrema was actually the right answer.

Given that we're solving a*t² + b*t + c in a case where a = 0, we actually solve 0 = b*t + c (assuming b doesn't also equal 0). When we apply the quadratic formula the division by 2*a is a problem when a is zero. So we fallback to solving the easier problem if we know a==0.

solve-cubic