From 5558ccd3bbbd0e689b775253309d381382f0aebb Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Mon, 31 Jan 2022 18:00:25 +0000 Subject: [PATCH] [colorLib.geometry] use math.isclose with relative tolerance to check circle inside-ness MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes rounding of radial gradient in 'SING OF THE HORNS' (medium-light skin tone) noto emoji 🤘🏼 --- Lib/fontTools/colorLib/geometry.py | 10 ++++------ Tests/colorLib/builder_test.py | 10 ++++++++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/Lib/fontTools/colorLib/geometry.py b/Lib/fontTools/colorLib/geometry.py index e62aead1c..1ce161bfa 100644 --- a/Lib/fontTools/colorLib/geometry.py +++ b/Lib/fontTools/colorLib/geometry.py @@ -1,6 +1,6 @@ """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 @@ -19,9 +19,7 @@ def _unit_vector(vec): return (vec[0] / length, vec[1] / length) -# This is the same tolerance used by Skia's SkTwoPointConicalGradient.cpp to detect -# when a radial gradient's focal point lies on the end circle. -_NEARLY_ZERO = 1 / (1 << 12) # 0.000244140625 +_CIRCLE_INSIDE_TOLERANCE = 1e-4 # The unit vector's X and Y components are respectively @@ -64,10 +62,10 @@ class Circle: def round(self): 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)) return ( - abs(outer_circle.radius - dist) <= _NEARLY_ZERO + isclose(outer_circle.radius, dist, rel_tol=_CIRCLE_INSIDE_TOLERANCE) or outer_circle.radius > dist ) diff --git a/Tests/colorLib/builder_test.py b/Tests/colorLib/builder_test.py index 9fa8fac7a..7259db4d8 100644 --- a/Tests/colorLib/builder_test.py +++ b/Tests/colorLib/builder_test.py @@ -1747,6 +1747,16 @@ class TrickyRadialGradientTest: r1 = 260.0072 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( "c0, r0, c1, r1, inside, expected", [