From 4dffdb7dd704d65ed257e874b8c2f053f1bd4fbd Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Mon, 7 Dec 2015 14:24:23 +0000 Subject: [PATCH 1/2] cu2qu.__init__: return (spline[s], error[s]) tuple from curve[s]_to_quadratic; raise exception if no approximation is found; ensure returned spline is a list of tuples --- Lib/cu2qu/__init__.py | 57 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/Lib/cu2qu/__init__.py b/Lib/cu2qu/__init__.py index aae6d36bf..0c15bc9d3 100644 --- a/Lib/cu2qu/__init__.py +++ b/Lib/cu2qu/__init__.py @@ -19,6 +19,22 @@ from math import hypot from fontTools.misc import bezierTools +class Cu2QuError(Exception): + pass + + +class ApproxNotFoundError(Cu2QuError): + def __init__(self, curve, error=None): + if error is None: + message = "no approximation found: %s" % curve + else: + message = ("approximation error exceeds max tolerance: %s, " + "error=%g" % (curve, error)) + super(Cu2QuError, self).__init__(message) + self.curve = curve + self.error = error + + def vector(p1, p2): """Return the vector from p1 to p2.""" return p2[0] - p1[0], p2[1] - p1[1] @@ -109,7 +125,7 @@ def cubic_approx_spline(p, n): p1 = calc_intersect(p) except ValueError: return None - return p[0], p1, p[3] + return [p[0], p1, p[3]] spline = [p[0]] ts = [i / n for i in range(1, n)] @@ -141,22 +157,45 @@ def curve_spline_dist(bezier, spline): def curve_to_quadratic(p, max_err, max_n): - """Return a quadratic spline approximating this cubic bezier.""" + """Return a quadratic spline approximating this cubic bezier, and + the error of approximation. + Raise 'ApproxNotFoundError' if no suitable approximation can be found + with the given parameters. + """ + spline, error = None, None for n in range(1, max_n + 1): spline = cubic_approx_spline(p, n) - if spline and curve_spline_dist(p, spline) <= max_err: + if spline is None: + continue + error = curve_spline_dist(p, spline) + if error <= max_err: break - return spline + else: + # no break: approximation not found or error exceeds tolerance + raise ApproxNotFoundError(p, error) + return spline, error def curves_to_quadratic(curves, max_errors, max_n): - """Return quadratic splines approximating these cubic beziers.""" + """Return quadratic splines approximating these cubic beziers, and + the respective errors of approximation. + Raise 'ApproxNotFoundError' if no suitable approximation can be found + for all curves with the given parameters. + """ + splines = [None] * len(curves) + errors = [None] * len(max_errors) for n in range(1, max_n + 1): splines = [cubic_approx_spline(c, n) for c in curves] - if (all(splines) and - all(curve_spline_dist(c, s) < max_err - for c, s, max_err in zip(curves, splines, max_errors))): + if not all(splines): + continue + errors = [curve_spline_dist(c, s) for c, s in zip(curves, splines)] + if all(err <= max_err for err, max_err in zip(errors, max_errors)): break - return splines + else: + # no break: raise if any spline is None or error exceeds tolerance + for c, s, error, max_err in zip(curves, splines, errors, max_errors): + if s is None or error > max_err: + raise ApproxNotFoundError(c, error) + return splines, errors From e6b47d61c91efff3b00b9560415fa5bcc34505ee Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Mon, 7 Dec 2015 14:42:57 +0000 Subject: [PATCH 2/2] cu2qu.rf: only get fist item returned from curve_to_quadratic, ignore approx error curve_to_quadratic and curves_to_quadratic now return a (spline, error) tuple. --- Lib/cu2qu/rf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/cu2qu/rf.py b/Lib/cu2qu/rf.py index 85c34ba09..e5b03fac7 100644 --- a/Lib/cu2qu/rf.py +++ b/Lib/cu2qu/rf.py @@ -118,10 +118,10 @@ def points_to_quadratic(p0, p1, p2, p3, max_err, max_n): if hasattr(p0, 'x'): curve = [(float(i.x), float(i.y)) for i in [p0, p1, p2, p3]] - return curve_to_quadratic(curve, max_err, max_n) + return curve_to_quadratic(curve, max_err, max_n)[0] curves = [[(float(i.x), float(i.y)) for i in p] for p in zip(p0, p1, p2, p3)] - return curves_to_quadratic(curves, max_err, max_n) + return curves_to_quadratic(curves, max_err, max_n)[0] def replace_segments(contour, segments):