[colorLib.geometry] use math.isclose with relative tolerance to check circle inside-ness

Fixes rounding of radial gradient in 'SING OF THE HORNS' (medium-light skin tone) noto emoji 🤘🏼
This commit is contained in:
Cosimo Lupo 2022-01-31 18:00:25 +00:00
parent 69fc06a1af
commit 5558ccd3bb
2 changed files with 14 additions and 6 deletions

View File

@ -1,6 +1,6 @@
"""Helpers for manipulating 2D points and vectors in COLR table.""" """Helpers for manipulating 2D points and vectors in COLR table."""
from math import copysign, cos, hypot, pi from math import copysign, cos, hypot, isclose, pi
from fontTools.misc.roundTools import otRound from fontTools.misc.roundTools import otRound
@ -19,9 +19,7 @@ def _unit_vector(vec):
return (vec[0] / length, vec[1] / length) return (vec[0] / length, vec[1] / length)
# This is the same tolerance used by Skia's SkTwoPointConicalGradient.cpp to detect _CIRCLE_INSIDE_TOLERANCE = 1e-4
# when a radial gradient's focal point lies on the end circle.
_NEARLY_ZERO = 1 / (1 << 12) # 0.000244140625
# The unit vector's X and Y components are respectively # The unit vector's X and Y components are respectively
@ -64,10 +62,10 @@ class Circle:
def round(self): def round(self):
return Circle(_round_point(self.centre), otRound(self.radius)) return Circle(_round_point(self.centre), otRound(self.radius))
def inside(self, outer_circle): def inside(self, outer_circle, tolerance=_CIRCLE_INSIDE_TOLERANCE):
dist = self.radius + hypot(*_vector_between(self.centre, outer_circle.centre)) dist = self.radius + hypot(*_vector_between(self.centre, outer_circle.centre))
return ( return (
abs(outer_circle.radius - dist) <= _NEARLY_ZERO isclose(outer_circle.radius, dist, rel_tol=_CIRCLE_INSIDE_TOLERANCE)
or outer_circle.radius > dist or outer_circle.radius > dist
) )

View File

@ -1747,6 +1747,16 @@ class TrickyRadialGradientTest:
r1 = 260.0072 r1 = 260.0072
assert self.round_start_circle(c0, r0, c1, r1, inside=True) == ((386, 71), 0) assert self.round_start_circle(c0, r0, c1, r1, inside=True) == ((386, 71), 0)
def test_noto_emoji_horns_sign_u1f918_1f3fc(self):
# This radial gradient is taken from noto-emoji's 'SIGNS OF THE HORNS'
# (1f918_1f3fc). We check that c0 is inside c1 both before and after rounding.
c0 = (-437.6789059060543, -2116.9237094478003)
r0 = 0.0
c1 = (-488.7330118252256, -1876.5036857045086)
r1 = 245.77147821915673
assert self.circle_inside_circle(c0, r0, c1, r1)
assert self.circle_inside_circle(c0, r0, c1, r1, rounded=True)
@pytest.mark.parametrize( @pytest.mark.parametrize(
"c0, r0, c1, r1, inside, expected", "c0, r0, c1, r1, inside, expected",
[ [