1*e1fe3e4aSElliott Hughes# cython: language_level=3 2*e1fe3e4aSElliott Hughes# distutils: define_macros=CYTHON_TRACE_NOGIL=1 3*e1fe3e4aSElliott Hughes 4*e1fe3e4aSElliott Hughes# Copyright 2015 Google Inc. All Rights Reserved. 5*e1fe3e4aSElliott Hughes# 6*e1fe3e4aSElliott Hughes# Licensed under the Apache License, Version 2.0 (the "License"); 7*e1fe3e4aSElliott Hughes# you may not use this file except in compliance with the License. 8*e1fe3e4aSElliott Hughes# You may obtain a copy of the License at 9*e1fe3e4aSElliott Hughes# 10*e1fe3e4aSElliott Hughes# http://www.apache.org/licenses/LICENSE-2.0 11*e1fe3e4aSElliott Hughes# 12*e1fe3e4aSElliott Hughes# Unless required by applicable law or agreed to in writing, software 13*e1fe3e4aSElliott Hughes# distributed under the License is distributed on an "AS IS" BASIS, 14*e1fe3e4aSElliott Hughes# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15*e1fe3e4aSElliott Hughes# See the License for the specific language governing permissions and 16*e1fe3e4aSElliott Hughes# limitations under the License. 17*e1fe3e4aSElliott Hughes 18*e1fe3e4aSElliott Hughestry: 19*e1fe3e4aSElliott Hughes import cython 20*e1fe3e4aSElliott Hughes 21*e1fe3e4aSElliott Hughes COMPILED = cython.compiled 22*e1fe3e4aSElliott Hughesexcept (AttributeError, ImportError): 23*e1fe3e4aSElliott Hughes # if cython not installed, use mock module with no-op decorators and types 24*e1fe3e4aSElliott Hughes from fontTools.misc import cython 25*e1fe3e4aSElliott Hughes 26*e1fe3e4aSElliott Hughes COMPILED = False 27*e1fe3e4aSElliott Hughes 28*e1fe3e4aSElliott Hughesimport math 29*e1fe3e4aSElliott Hughes 30*e1fe3e4aSElliott Hughesfrom .errors import Error as Cu2QuError, ApproxNotFoundError 31*e1fe3e4aSElliott Hughes 32*e1fe3e4aSElliott Hughes 33*e1fe3e4aSElliott Hughes__all__ = ["curve_to_quadratic", "curves_to_quadratic"] 34*e1fe3e4aSElliott Hughes 35*e1fe3e4aSElliott HughesMAX_N = 100 36*e1fe3e4aSElliott Hughes 37*e1fe3e4aSElliott HughesNAN = float("NaN") 38*e1fe3e4aSElliott Hughes 39*e1fe3e4aSElliott Hughes 40*e1fe3e4aSElliott Hughes@cython.cfunc 41*e1fe3e4aSElliott Hughes@cython.inline 42*e1fe3e4aSElliott Hughes@cython.returns(cython.double) 43*e1fe3e4aSElliott Hughes@cython.locals(v1=cython.complex, v2=cython.complex) 44*e1fe3e4aSElliott Hughesdef dot(v1, v2): 45*e1fe3e4aSElliott Hughes """Return the dot product of two vectors. 46*e1fe3e4aSElliott Hughes 47*e1fe3e4aSElliott Hughes Args: 48*e1fe3e4aSElliott Hughes v1 (complex): First vector. 49*e1fe3e4aSElliott Hughes v2 (complex): Second vector. 50*e1fe3e4aSElliott Hughes 51*e1fe3e4aSElliott Hughes Returns: 52*e1fe3e4aSElliott Hughes double: Dot product. 53*e1fe3e4aSElliott Hughes """ 54*e1fe3e4aSElliott Hughes return (v1 * v2.conjugate()).real 55*e1fe3e4aSElliott Hughes 56*e1fe3e4aSElliott Hughes 57*e1fe3e4aSElliott Hughes@cython.cfunc 58*e1fe3e4aSElliott Hughes@cython.inline 59*e1fe3e4aSElliott Hughes@cython.locals(a=cython.complex, b=cython.complex, c=cython.complex, d=cython.complex) 60*e1fe3e4aSElliott Hughes@cython.locals( 61*e1fe3e4aSElliott Hughes _1=cython.complex, _2=cython.complex, _3=cython.complex, _4=cython.complex 62*e1fe3e4aSElliott Hughes) 63*e1fe3e4aSElliott Hughesdef calc_cubic_points(a, b, c, d): 64*e1fe3e4aSElliott Hughes _1 = d 65*e1fe3e4aSElliott Hughes _2 = (c / 3.0) + d 66*e1fe3e4aSElliott Hughes _3 = (b + c) / 3.0 + _2 67*e1fe3e4aSElliott Hughes _4 = a + d + c + b 68*e1fe3e4aSElliott Hughes return _1, _2, _3, _4 69*e1fe3e4aSElliott Hughes 70*e1fe3e4aSElliott Hughes 71*e1fe3e4aSElliott Hughes@cython.cfunc 72*e1fe3e4aSElliott Hughes@cython.inline 73*e1fe3e4aSElliott Hughes@cython.locals( 74*e1fe3e4aSElliott Hughes p0=cython.complex, p1=cython.complex, p2=cython.complex, p3=cython.complex 75*e1fe3e4aSElliott Hughes) 76*e1fe3e4aSElliott Hughes@cython.locals(a=cython.complex, b=cython.complex, c=cython.complex, d=cython.complex) 77*e1fe3e4aSElliott Hughesdef calc_cubic_parameters(p0, p1, p2, p3): 78*e1fe3e4aSElliott Hughes c = (p1 - p0) * 3.0 79*e1fe3e4aSElliott Hughes b = (p2 - p1) * 3.0 - c 80*e1fe3e4aSElliott Hughes d = p0 81*e1fe3e4aSElliott Hughes a = p3 - d - c - b 82*e1fe3e4aSElliott Hughes return a, b, c, d 83*e1fe3e4aSElliott Hughes 84*e1fe3e4aSElliott Hughes 85*e1fe3e4aSElliott Hughes@cython.cfunc 86*e1fe3e4aSElliott Hughes@cython.inline 87*e1fe3e4aSElliott Hughes@cython.locals( 88*e1fe3e4aSElliott Hughes p0=cython.complex, p1=cython.complex, p2=cython.complex, p3=cython.complex 89*e1fe3e4aSElliott Hughes) 90*e1fe3e4aSElliott Hughesdef split_cubic_into_n_iter(p0, p1, p2, p3, n): 91*e1fe3e4aSElliott Hughes """Split a cubic Bezier into n equal parts. 92*e1fe3e4aSElliott Hughes 93*e1fe3e4aSElliott Hughes Splits the curve into `n` equal parts by curve time. 94*e1fe3e4aSElliott Hughes (t=0..1/n, t=1/n..2/n, ...) 95*e1fe3e4aSElliott Hughes 96*e1fe3e4aSElliott Hughes Args: 97*e1fe3e4aSElliott Hughes p0 (complex): Start point of curve. 98*e1fe3e4aSElliott Hughes p1 (complex): First handle of curve. 99*e1fe3e4aSElliott Hughes p2 (complex): Second handle of curve. 100*e1fe3e4aSElliott Hughes p3 (complex): End point of curve. 101*e1fe3e4aSElliott Hughes 102*e1fe3e4aSElliott Hughes Returns: 103*e1fe3e4aSElliott Hughes An iterator yielding the control points (four complex values) of the 104*e1fe3e4aSElliott Hughes subcurves. 105*e1fe3e4aSElliott Hughes """ 106*e1fe3e4aSElliott Hughes # Hand-coded special-cases 107*e1fe3e4aSElliott Hughes if n == 2: 108*e1fe3e4aSElliott Hughes return iter(split_cubic_into_two(p0, p1, p2, p3)) 109*e1fe3e4aSElliott Hughes if n == 3: 110*e1fe3e4aSElliott Hughes return iter(split_cubic_into_three(p0, p1, p2, p3)) 111*e1fe3e4aSElliott Hughes if n == 4: 112*e1fe3e4aSElliott Hughes a, b = split_cubic_into_two(p0, p1, p2, p3) 113*e1fe3e4aSElliott Hughes return iter( 114*e1fe3e4aSElliott Hughes split_cubic_into_two(a[0], a[1], a[2], a[3]) 115*e1fe3e4aSElliott Hughes + split_cubic_into_two(b[0], b[1], b[2], b[3]) 116*e1fe3e4aSElliott Hughes ) 117*e1fe3e4aSElliott Hughes if n == 6: 118*e1fe3e4aSElliott Hughes a, b = split_cubic_into_two(p0, p1, p2, p3) 119*e1fe3e4aSElliott Hughes return iter( 120*e1fe3e4aSElliott Hughes split_cubic_into_three(a[0], a[1], a[2], a[3]) 121*e1fe3e4aSElliott Hughes + split_cubic_into_three(b[0], b[1], b[2], b[3]) 122*e1fe3e4aSElliott Hughes ) 123*e1fe3e4aSElliott Hughes 124*e1fe3e4aSElliott Hughes return _split_cubic_into_n_gen(p0, p1, p2, p3, n) 125*e1fe3e4aSElliott Hughes 126*e1fe3e4aSElliott Hughes 127*e1fe3e4aSElliott Hughes@cython.locals( 128*e1fe3e4aSElliott Hughes p0=cython.complex, 129*e1fe3e4aSElliott Hughes p1=cython.complex, 130*e1fe3e4aSElliott Hughes p2=cython.complex, 131*e1fe3e4aSElliott Hughes p3=cython.complex, 132*e1fe3e4aSElliott Hughes n=cython.int, 133*e1fe3e4aSElliott Hughes) 134*e1fe3e4aSElliott Hughes@cython.locals(a=cython.complex, b=cython.complex, c=cython.complex, d=cython.complex) 135*e1fe3e4aSElliott Hughes@cython.locals( 136*e1fe3e4aSElliott Hughes dt=cython.double, delta_2=cython.double, delta_3=cython.double, i=cython.int 137*e1fe3e4aSElliott Hughes) 138*e1fe3e4aSElliott Hughes@cython.locals( 139*e1fe3e4aSElliott Hughes a1=cython.complex, b1=cython.complex, c1=cython.complex, d1=cython.complex 140*e1fe3e4aSElliott Hughes) 141*e1fe3e4aSElliott Hughesdef _split_cubic_into_n_gen(p0, p1, p2, p3, n): 142*e1fe3e4aSElliott Hughes a, b, c, d = calc_cubic_parameters(p0, p1, p2, p3) 143*e1fe3e4aSElliott Hughes dt = 1 / n 144*e1fe3e4aSElliott Hughes delta_2 = dt * dt 145*e1fe3e4aSElliott Hughes delta_3 = dt * delta_2 146*e1fe3e4aSElliott Hughes for i in range(n): 147*e1fe3e4aSElliott Hughes t1 = i * dt 148*e1fe3e4aSElliott Hughes t1_2 = t1 * t1 149*e1fe3e4aSElliott Hughes # calc new a, b, c and d 150*e1fe3e4aSElliott Hughes a1 = a * delta_3 151*e1fe3e4aSElliott Hughes b1 = (3 * a * t1 + b) * delta_2 152*e1fe3e4aSElliott Hughes c1 = (2 * b * t1 + c + 3 * a * t1_2) * dt 153*e1fe3e4aSElliott Hughes d1 = a * t1 * t1_2 + b * t1_2 + c * t1 + d 154*e1fe3e4aSElliott Hughes yield calc_cubic_points(a1, b1, c1, d1) 155*e1fe3e4aSElliott Hughes 156*e1fe3e4aSElliott Hughes 157*e1fe3e4aSElliott Hughes@cython.cfunc 158*e1fe3e4aSElliott Hughes@cython.inline 159*e1fe3e4aSElliott Hughes@cython.locals( 160*e1fe3e4aSElliott Hughes p0=cython.complex, p1=cython.complex, p2=cython.complex, p3=cython.complex 161*e1fe3e4aSElliott Hughes) 162*e1fe3e4aSElliott Hughes@cython.locals(mid=cython.complex, deriv3=cython.complex) 163*e1fe3e4aSElliott Hughesdef split_cubic_into_two(p0, p1, p2, p3): 164*e1fe3e4aSElliott Hughes """Split a cubic Bezier into two equal parts. 165*e1fe3e4aSElliott Hughes 166*e1fe3e4aSElliott Hughes Splits the curve into two equal parts at t = 0.5 167*e1fe3e4aSElliott Hughes 168*e1fe3e4aSElliott Hughes Args: 169*e1fe3e4aSElliott Hughes p0 (complex): Start point of curve. 170*e1fe3e4aSElliott Hughes p1 (complex): First handle of curve. 171*e1fe3e4aSElliott Hughes p2 (complex): Second handle of curve. 172*e1fe3e4aSElliott Hughes p3 (complex): End point of curve. 173*e1fe3e4aSElliott Hughes 174*e1fe3e4aSElliott Hughes Returns: 175*e1fe3e4aSElliott Hughes tuple: Two cubic Beziers (each expressed as a tuple of four complex 176*e1fe3e4aSElliott Hughes values). 177*e1fe3e4aSElliott Hughes """ 178*e1fe3e4aSElliott Hughes mid = (p0 + 3 * (p1 + p2) + p3) * 0.125 179*e1fe3e4aSElliott Hughes deriv3 = (p3 + p2 - p1 - p0) * 0.125 180*e1fe3e4aSElliott Hughes return ( 181*e1fe3e4aSElliott Hughes (p0, (p0 + p1) * 0.5, mid - deriv3, mid), 182*e1fe3e4aSElliott Hughes (mid, mid + deriv3, (p2 + p3) * 0.5, p3), 183*e1fe3e4aSElliott Hughes ) 184*e1fe3e4aSElliott Hughes 185*e1fe3e4aSElliott Hughes 186*e1fe3e4aSElliott Hughes@cython.cfunc 187*e1fe3e4aSElliott Hughes@cython.inline 188*e1fe3e4aSElliott Hughes@cython.locals( 189*e1fe3e4aSElliott Hughes p0=cython.complex, 190*e1fe3e4aSElliott Hughes p1=cython.complex, 191*e1fe3e4aSElliott Hughes p2=cython.complex, 192*e1fe3e4aSElliott Hughes p3=cython.complex, 193*e1fe3e4aSElliott Hughes) 194*e1fe3e4aSElliott Hughes@cython.locals( 195*e1fe3e4aSElliott Hughes mid1=cython.complex, 196*e1fe3e4aSElliott Hughes deriv1=cython.complex, 197*e1fe3e4aSElliott Hughes mid2=cython.complex, 198*e1fe3e4aSElliott Hughes deriv2=cython.complex, 199*e1fe3e4aSElliott Hughes) 200*e1fe3e4aSElliott Hughesdef split_cubic_into_three(p0, p1, p2, p3): 201*e1fe3e4aSElliott Hughes """Split a cubic Bezier into three equal parts. 202*e1fe3e4aSElliott Hughes 203*e1fe3e4aSElliott Hughes Splits the curve into three equal parts at t = 1/3 and t = 2/3 204*e1fe3e4aSElliott Hughes 205*e1fe3e4aSElliott Hughes Args: 206*e1fe3e4aSElliott Hughes p0 (complex): Start point of curve. 207*e1fe3e4aSElliott Hughes p1 (complex): First handle of curve. 208*e1fe3e4aSElliott Hughes p2 (complex): Second handle of curve. 209*e1fe3e4aSElliott Hughes p3 (complex): End point of curve. 210*e1fe3e4aSElliott Hughes 211*e1fe3e4aSElliott Hughes Returns: 212*e1fe3e4aSElliott Hughes tuple: Three cubic Beziers (each expressed as a tuple of four complex 213*e1fe3e4aSElliott Hughes values). 214*e1fe3e4aSElliott Hughes """ 215*e1fe3e4aSElliott Hughes mid1 = (8 * p0 + 12 * p1 + 6 * p2 + p3) * (1 / 27) 216*e1fe3e4aSElliott Hughes deriv1 = (p3 + 3 * p2 - 4 * p0) * (1 / 27) 217*e1fe3e4aSElliott Hughes mid2 = (p0 + 6 * p1 + 12 * p2 + 8 * p3) * (1 / 27) 218*e1fe3e4aSElliott Hughes deriv2 = (4 * p3 - 3 * p1 - p0) * (1 / 27) 219*e1fe3e4aSElliott Hughes return ( 220*e1fe3e4aSElliott Hughes (p0, (2 * p0 + p1) / 3.0, mid1 - deriv1, mid1), 221*e1fe3e4aSElliott Hughes (mid1, mid1 + deriv1, mid2 - deriv2, mid2), 222*e1fe3e4aSElliott Hughes (mid2, mid2 + deriv2, (p2 + 2 * p3) / 3.0, p3), 223*e1fe3e4aSElliott Hughes ) 224*e1fe3e4aSElliott Hughes 225*e1fe3e4aSElliott Hughes 226*e1fe3e4aSElliott Hughes@cython.cfunc 227*e1fe3e4aSElliott Hughes@cython.inline 228*e1fe3e4aSElliott Hughes@cython.returns(cython.complex) 229*e1fe3e4aSElliott Hughes@cython.locals( 230*e1fe3e4aSElliott Hughes t=cython.double, 231*e1fe3e4aSElliott Hughes p0=cython.complex, 232*e1fe3e4aSElliott Hughes p1=cython.complex, 233*e1fe3e4aSElliott Hughes p2=cython.complex, 234*e1fe3e4aSElliott Hughes p3=cython.complex, 235*e1fe3e4aSElliott Hughes) 236*e1fe3e4aSElliott Hughes@cython.locals(_p1=cython.complex, _p2=cython.complex) 237*e1fe3e4aSElliott Hughesdef cubic_approx_control(t, p0, p1, p2, p3): 238*e1fe3e4aSElliott Hughes """Approximate a cubic Bezier using a quadratic one. 239*e1fe3e4aSElliott Hughes 240*e1fe3e4aSElliott Hughes Args: 241*e1fe3e4aSElliott Hughes t (double): Position of control point. 242*e1fe3e4aSElliott Hughes p0 (complex): Start point of curve. 243*e1fe3e4aSElliott Hughes p1 (complex): First handle of curve. 244*e1fe3e4aSElliott Hughes p2 (complex): Second handle of curve. 245*e1fe3e4aSElliott Hughes p3 (complex): End point of curve. 246*e1fe3e4aSElliott Hughes 247*e1fe3e4aSElliott Hughes Returns: 248*e1fe3e4aSElliott Hughes complex: Location of candidate control point on quadratic curve. 249*e1fe3e4aSElliott Hughes """ 250*e1fe3e4aSElliott Hughes _p1 = p0 + (p1 - p0) * 1.5 251*e1fe3e4aSElliott Hughes _p2 = p3 + (p2 - p3) * 1.5 252*e1fe3e4aSElliott Hughes return _p1 + (_p2 - _p1) * t 253*e1fe3e4aSElliott Hughes 254*e1fe3e4aSElliott Hughes 255*e1fe3e4aSElliott Hughes@cython.cfunc 256*e1fe3e4aSElliott Hughes@cython.inline 257*e1fe3e4aSElliott Hughes@cython.returns(cython.complex) 258*e1fe3e4aSElliott Hughes@cython.locals(a=cython.complex, b=cython.complex, c=cython.complex, d=cython.complex) 259*e1fe3e4aSElliott Hughes@cython.locals(ab=cython.complex, cd=cython.complex, p=cython.complex, h=cython.double) 260*e1fe3e4aSElliott Hughesdef calc_intersect(a, b, c, d): 261*e1fe3e4aSElliott Hughes """Calculate the intersection of two lines. 262*e1fe3e4aSElliott Hughes 263*e1fe3e4aSElliott Hughes Args: 264*e1fe3e4aSElliott Hughes a (complex): Start point of first line. 265*e1fe3e4aSElliott Hughes b (complex): End point of first line. 266*e1fe3e4aSElliott Hughes c (complex): Start point of second line. 267*e1fe3e4aSElliott Hughes d (complex): End point of second line. 268*e1fe3e4aSElliott Hughes 269*e1fe3e4aSElliott Hughes Returns: 270*e1fe3e4aSElliott Hughes complex: Location of intersection if one present, ``complex(NaN,NaN)`` 271*e1fe3e4aSElliott Hughes if no intersection was found. 272*e1fe3e4aSElliott Hughes """ 273*e1fe3e4aSElliott Hughes ab = b - a 274*e1fe3e4aSElliott Hughes cd = d - c 275*e1fe3e4aSElliott Hughes p = ab * 1j 276*e1fe3e4aSElliott Hughes try: 277*e1fe3e4aSElliott Hughes h = dot(p, a - c) / dot(p, cd) 278*e1fe3e4aSElliott Hughes except ZeroDivisionError: 279*e1fe3e4aSElliott Hughes return complex(NAN, NAN) 280*e1fe3e4aSElliott Hughes return c + cd * h 281*e1fe3e4aSElliott Hughes 282*e1fe3e4aSElliott Hughes 283*e1fe3e4aSElliott Hughes@cython.cfunc 284*e1fe3e4aSElliott Hughes@cython.returns(cython.int) 285*e1fe3e4aSElliott Hughes@cython.locals( 286*e1fe3e4aSElliott Hughes tolerance=cython.double, 287*e1fe3e4aSElliott Hughes p0=cython.complex, 288*e1fe3e4aSElliott Hughes p1=cython.complex, 289*e1fe3e4aSElliott Hughes p2=cython.complex, 290*e1fe3e4aSElliott Hughes p3=cython.complex, 291*e1fe3e4aSElliott Hughes) 292*e1fe3e4aSElliott Hughes@cython.locals(mid=cython.complex, deriv3=cython.complex) 293*e1fe3e4aSElliott Hughesdef cubic_farthest_fit_inside(p0, p1, p2, p3, tolerance): 294*e1fe3e4aSElliott Hughes """Check if a cubic Bezier lies within a given distance of the origin. 295*e1fe3e4aSElliott Hughes 296*e1fe3e4aSElliott Hughes "Origin" means *the* origin (0,0), not the start of the curve. Note that no 297*e1fe3e4aSElliott Hughes checks are made on the start and end positions of the curve; this function 298*e1fe3e4aSElliott Hughes only checks the inside of the curve. 299*e1fe3e4aSElliott Hughes 300*e1fe3e4aSElliott Hughes Args: 301*e1fe3e4aSElliott Hughes p0 (complex): Start point of curve. 302*e1fe3e4aSElliott Hughes p1 (complex): First handle of curve. 303*e1fe3e4aSElliott Hughes p2 (complex): Second handle of curve. 304*e1fe3e4aSElliott Hughes p3 (complex): End point of curve. 305*e1fe3e4aSElliott Hughes tolerance (double): Distance from origin. 306*e1fe3e4aSElliott Hughes 307*e1fe3e4aSElliott Hughes Returns: 308*e1fe3e4aSElliott Hughes bool: True if the cubic Bezier ``p`` entirely lies within a distance 309*e1fe3e4aSElliott Hughes ``tolerance`` of the origin, False otherwise. 310*e1fe3e4aSElliott Hughes """ 311*e1fe3e4aSElliott Hughes # First check p2 then p1, as p2 has higher error early on. 312*e1fe3e4aSElliott Hughes if abs(p2) <= tolerance and abs(p1) <= tolerance: 313*e1fe3e4aSElliott Hughes return True 314*e1fe3e4aSElliott Hughes 315*e1fe3e4aSElliott Hughes # Split. 316*e1fe3e4aSElliott Hughes mid = (p0 + 3 * (p1 + p2) + p3) * 0.125 317*e1fe3e4aSElliott Hughes if abs(mid) > tolerance: 318*e1fe3e4aSElliott Hughes return False 319*e1fe3e4aSElliott Hughes deriv3 = (p3 + p2 - p1 - p0) * 0.125 320*e1fe3e4aSElliott Hughes return cubic_farthest_fit_inside( 321*e1fe3e4aSElliott Hughes p0, (p0 + p1) * 0.5, mid - deriv3, mid, tolerance 322*e1fe3e4aSElliott Hughes ) and cubic_farthest_fit_inside(mid, mid + deriv3, (p2 + p3) * 0.5, p3, tolerance) 323*e1fe3e4aSElliott Hughes 324*e1fe3e4aSElliott Hughes 325*e1fe3e4aSElliott Hughes@cython.cfunc 326*e1fe3e4aSElliott Hughes@cython.inline 327*e1fe3e4aSElliott Hughes@cython.locals(tolerance=cython.double) 328*e1fe3e4aSElliott Hughes@cython.locals( 329*e1fe3e4aSElliott Hughes q1=cython.complex, 330*e1fe3e4aSElliott Hughes c0=cython.complex, 331*e1fe3e4aSElliott Hughes c1=cython.complex, 332*e1fe3e4aSElliott Hughes c2=cython.complex, 333*e1fe3e4aSElliott Hughes c3=cython.complex, 334*e1fe3e4aSElliott Hughes) 335*e1fe3e4aSElliott Hughesdef cubic_approx_quadratic(cubic, tolerance): 336*e1fe3e4aSElliott Hughes """Approximate a cubic Bezier with a single quadratic within a given tolerance. 337*e1fe3e4aSElliott Hughes 338*e1fe3e4aSElliott Hughes Args: 339*e1fe3e4aSElliott Hughes cubic (sequence): Four complex numbers representing control points of 340*e1fe3e4aSElliott Hughes the cubic Bezier curve. 341*e1fe3e4aSElliott Hughes tolerance (double): Permitted deviation from the original curve. 342*e1fe3e4aSElliott Hughes 343*e1fe3e4aSElliott Hughes Returns: 344*e1fe3e4aSElliott Hughes Three complex numbers representing control points of the quadratic 345*e1fe3e4aSElliott Hughes curve if it fits within the given tolerance, or ``None`` if no suitable 346*e1fe3e4aSElliott Hughes curve could be calculated. 347*e1fe3e4aSElliott Hughes """ 348*e1fe3e4aSElliott Hughes 349*e1fe3e4aSElliott Hughes q1 = calc_intersect(cubic[0], cubic[1], cubic[2], cubic[3]) 350*e1fe3e4aSElliott Hughes if math.isnan(q1.imag): 351*e1fe3e4aSElliott Hughes return None 352*e1fe3e4aSElliott Hughes c0 = cubic[0] 353*e1fe3e4aSElliott Hughes c3 = cubic[3] 354*e1fe3e4aSElliott Hughes c1 = c0 + (q1 - c0) * (2 / 3) 355*e1fe3e4aSElliott Hughes c2 = c3 + (q1 - c3) * (2 / 3) 356*e1fe3e4aSElliott Hughes if not cubic_farthest_fit_inside(0, c1 - cubic[1], c2 - cubic[2], 0, tolerance): 357*e1fe3e4aSElliott Hughes return None 358*e1fe3e4aSElliott Hughes return c0, q1, c3 359*e1fe3e4aSElliott Hughes 360*e1fe3e4aSElliott Hughes 361*e1fe3e4aSElliott Hughes@cython.cfunc 362*e1fe3e4aSElliott Hughes@cython.locals(n=cython.int, tolerance=cython.double) 363*e1fe3e4aSElliott Hughes@cython.locals(i=cython.int) 364*e1fe3e4aSElliott Hughes@cython.locals(all_quadratic=cython.int) 365*e1fe3e4aSElliott Hughes@cython.locals( 366*e1fe3e4aSElliott Hughes c0=cython.complex, c1=cython.complex, c2=cython.complex, c3=cython.complex 367*e1fe3e4aSElliott Hughes) 368*e1fe3e4aSElliott Hughes@cython.locals( 369*e1fe3e4aSElliott Hughes q0=cython.complex, 370*e1fe3e4aSElliott Hughes q1=cython.complex, 371*e1fe3e4aSElliott Hughes next_q1=cython.complex, 372*e1fe3e4aSElliott Hughes q2=cython.complex, 373*e1fe3e4aSElliott Hughes d1=cython.complex, 374*e1fe3e4aSElliott Hughes) 375*e1fe3e4aSElliott Hughesdef cubic_approx_spline(cubic, n, tolerance, all_quadratic): 376*e1fe3e4aSElliott Hughes """Approximate a cubic Bezier curve with a spline of n quadratics. 377*e1fe3e4aSElliott Hughes 378*e1fe3e4aSElliott Hughes Args: 379*e1fe3e4aSElliott Hughes cubic (sequence): Four complex numbers representing control points of 380*e1fe3e4aSElliott Hughes the cubic Bezier curve. 381*e1fe3e4aSElliott Hughes n (int): Number of quadratic Bezier curves in the spline. 382*e1fe3e4aSElliott Hughes tolerance (double): Permitted deviation from the original curve. 383*e1fe3e4aSElliott Hughes 384*e1fe3e4aSElliott Hughes Returns: 385*e1fe3e4aSElliott Hughes A list of ``n+2`` complex numbers, representing control points of the 386*e1fe3e4aSElliott Hughes quadratic spline if it fits within the given tolerance, or ``None`` if 387*e1fe3e4aSElliott Hughes no suitable spline could be calculated. 388*e1fe3e4aSElliott Hughes """ 389*e1fe3e4aSElliott Hughes 390*e1fe3e4aSElliott Hughes if n == 1: 391*e1fe3e4aSElliott Hughes return cubic_approx_quadratic(cubic, tolerance) 392*e1fe3e4aSElliott Hughes if n == 2 and all_quadratic == False: 393*e1fe3e4aSElliott Hughes return cubic 394*e1fe3e4aSElliott Hughes 395*e1fe3e4aSElliott Hughes cubics = split_cubic_into_n_iter(cubic[0], cubic[1], cubic[2], cubic[3], n) 396*e1fe3e4aSElliott Hughes 397*e1fe3e4aSElliott Hughes # calculate the spline of quadratics and check errors at the same time. 398*e1fe3e4aSElliott Hughes next_cubic = next(cubics) 399*e1fe3e4aSElliott Hughes next_q1 = cubic_approx_control( 400*e1fe3e4aSElliott Hughes 0, next_cubic[0], next_cubic[1], next_cubic[2], next_cubic[3] 401*e1fe3e4aSElliott Hughes ) 402*e1fe3e4aSElliott Hughes q2 = cubic[0] 403*e1fe3e4aSElliott Hughes d1 = 0j 404*e1fe3e4aSElliott Hughes spline = [cubic[0], next_q1] 405*e1fe3e4aSElliott Hughes for i in range(1, n + 1): 406*e1fe3e4aSElliott Hughes # Current cubic to convert 407*e1fe3e4aSElliott Hughes c0, c1, c2, c3 = next_cubic 408*e1fe3e4aSElliott Hughes 409*e1fe3e4aSElliott Hughes # Current quadratic approximation of current cubic 410*e1fe3e4aSElliott Hughes q0 = q2 411*e1fe3e4aSElliott Hughes q1 = next_q1 412*e1fe3e4aSElliott Hughes if i < n: 413*e1fe3e4aSElliott Hughes next_cubic = next(cubics) 414*e1fe3e4aSElliott Hughes next_q1 = cubic_approx_control( 415*e1fe3e4aSElliott Hughes i / (n - 1), next_cubic[0], next_cubic[1], next_cubic[2], next_cubic[3] 416*e1fe3e4aSElliott Hughes ) 417*e1fe3e4aSElliott Hughes spline.append(next_q1) 418*e1fe3e4aSElliott Hughes q2 = (q1 + next_q1) * 0.5 419*e1fe3e4aSElliott Hughes else: 420*e1fe3e4aSElliott Hughes q2 = c3 421*e1fe3e4aSElliott Hughes 422*e1fe3e4aSElliott Hughes # End-point deltas 423*e1fe3e4aSElliott Hughes d0 = d1 424*e1fe3e4aSElliott Hughes d1 = q2 - c3 425*e1fe3e4aSElliott Hughes 426*e1fe3e4aSElliott Hughes if abs(d1) > tolerance or not cubic_farthest_fit_inside( 427*e1fe3e4aSElliott Hughes d0, 428*e1fe3e4aSElliott Hughes q0 + (q1 - q0) * (2 / 3) - c1, 429*e1fe3e4aSElliott Hughes q2 + (q1 - q2) * (2 / 3) - c2, 430*e1fe3e4aSElliott Hughes d1, 431*e1fe3e4aSElliott Hughes tolerance, 432*e1fe3e4aSElliott Hughes ): 433*e1fe3e4aSElliott Hughes return None 434*e1fe3e4aSElliott Hughes spline.append(cubic[3]) 435*e1fe3e4aSElliott Hughes 436*e1fe3e4aSElliott Hughes return spline 437*e1fe3e4aSElliott Hughes 438*e1fe3e4aSElliott Hughes 439*e1fe3e4aSElliott Hughes@cython.locals(max_err=cython.double) 440*e1fe3e4aSElliott Hughes@cython.locals(n=cython.int) 441*e1fe3e4aSElliott Hughes@cython.locals(all_quadratic=cython.int) 442*e1fe3e4aSElliott Hughesdef curve_to_quadratic(curve, max_err, all_quadratic=True): 443*e1fe3e4aSElliott Hughes """Approximate a cubic Bezier curve with a spline of n quadratics. 444*e1fe3e4aSElliott Hughes 445*e1fe3e4aSElliott Hughes Args: 446*e1fe3e4aSElliott Hughes cubic (sequence): Four 2D tuples representing control points of 447*e1fe3e4aSElliott Hughes the cubic Bezier curve. 448*e1fe3e4aSElliott Hughes max_err (double): Permitted deviation from the original curve. 449*e1fe3e4aSElliott Hughes all_quadratic (bool): If True (default) returned value is a 450*e1fe3e4aSElliott Hughes quadratic spline. If False, it's either a single quadratic 451*e1fe3e4aSElliott Hughes curve or a single cubic curve. 452*e1fe3e4aSElliott Hughes 453*e1fe3e4aSElliott Hughes Returns: 454*e1fe3e4aSElliott Hughes If all_quadratic is True: A list of 2D tuples, representing 455*e1fe3e4aSElliott Hughes control points of the quadratic spline if it fits within the 456*e1fe3e4aSElliott Hughes given tolerance, or ``None`` if no suitable spline could be 457*e1fe3e4aSElliott Hughes calculated. 458*e1fe3e4aSElliott Hughes 459*e1fe3e4aSElliott Hughes If all_quadratic is False: Either a quadratic curve (if length 460*e1fe3e4aSElliott Hughes of output is 3), or a cubic curve (if length of output is 4). 461*e1fe3e4aSElliott Hughes """ 462*e1fe3e4aSElliott Hughes 463*e1fe3e4aSElliott Hughes curve = [complex(*p) for p in curve] 464*e1fe3e4aSElliott Hughes 465*e1fe3e4aSElliott Hughes for n in range(1, MAX_N + 1): 466*e1fe3e4aSElliott Hughes spline = cubic_approx_spline(curve, n, max_err, all_quadratic) 467*e1fe3e4aSElliott Hughes if spline is not None: 468*e1fe3e4aSElliott Hughes # done. go home 469*e1fe3e4aSElliott Hughes return [(s.real, s.imag) for s in spline] 470*e1fe3e4aSElliott Hughes 471*e1fe3e4aSElliott Hughes raise ApproxNotFoundError(curve) 472*e1fe3e4aSElliott Hughes 473*e1fe3e4aSElliott Hughes 474*e1fe3e4aSElliott Hughes@cython.locals(l=cython.int, last_i=cython.int, i=cython.int) 475*e1fe3e4aSElliott Hughes@cython.locals(all_quadratic=cython.int) 476*e1fe3e4aSElliott Hughesdef curves_to_quadratic(curves, max_errors, all_quadratic=True): 477*e1fe3e4aSElliott Hughes """Return quadratic Bezier splines approximating the input cubic Beziers. 478*e1fe3e4aSElliott Hughes 479*e1fe3e4aSElliott Hughes Args: 480*e1fe3e4aSElliott Hughes curves: A sequence of *n* curves, each curve being a sequence of four 481*e1fe3e4aSElliott Hughes 2D tuples. 482*e1fe3e4aSElliott Hughes max_errors: A sequence of *n* floats representing the maximum permissible 483*e1fe3e4aSElliott Hughes deviation from each of the cubic Bezier curves. 484*e1fe3e4aSElliott Hughes all_quadratic (bool): If True (default) returned values are a 485*e1fe3e4aSElliott Hughes quadratic spline. If False, they are either a single quadratic 486*e1fe3e4aSElliott Hughes curve or a single cubic curve. 487*e1fe3e4aSElliott Hughes 488*e1fe3e4aSElliott Hughes Example:: 489*e1fe3e4aSElliott Hughes 490*e1fe3e4aSElliott Hughes >>> curves_to_quadratic( [ 491*e1fe3e4aSElliott Hughes ... [ (50,50), (100,100), (150,100), (200,50) ], 492*e1fe3e4aSElliott Hughes ... [ (75,50), (120,100), (150,75), (200,60) ] 493*e1fe3e4aSElliott Hughes ... ], [1,1] ) 494*e1fe3e4aSElliott Hughes [[(50.0, 50.0), (75.0, 75.0), (125.0, 91.66666666666666), (175.0, 75.0), (200.0, 50.0)], [(75.0, 50.0), (97.5, 75.0), (135.41666666666666, 82.08333333333333), (175.0, 67.5), (200.0, 60.0)]] 495*e1fe3e4aSElliott Hughes 496*e1fe3e4aSElliott Hughes The returned splines have "implied oncurve points" suitable for use in 497*e1fe3e4aSElliott Hughes TrueType ``glif`` outlines - i.e. in the first spline returned above, 498*e1fe3e4aSElliott Hughes the first quadratic segment runs from (50,50) to 499*e1fe3e4aSElliott Hughes ( (75 + 125)/2 , (120 + 91.666..)/2 ) = (100, 83.333...). 500*e1fe3e4aSElliott Hughes 501*e1fe3e4aSElliott Hughes Returns: 502*e1fe3e4aSElliott Hughes If all_quadratic is True, a list of splines, each spline being a list 503*e1fe3e4aSElliott Hughes of 2D tuples. 504*e1fe3e4aSElliott Hughes 505*e1fe3e4aSElliott Hughes If all_quadratic is False, a list of curves, each curve being a quadratic 506*e1fe3e4aSElliott Hughes (length 3), or cubic (length 4). 507*e1fe3e4aSElliott Hughes 508*e1fe3e4aSElliott Hughes Raises: 509*e1fe3e4aSElliott Hughes fontTools.cu2qu.Errors.ApproxNotFoundError: if no suitable approximation 510*e1fe3e4aSElliott Hughes can be found for all curves with the given parameters. 511*e1fe3e4aSElliott Hughes """ 512*e1fe3e4aSElliott Hughes 513*e1fe3e4aSElliott Hughes curves = [[complex(*p) for p in curve] for curve in curves] 514*e1fe3e4aSElliott Hughes assert len(max_errors) == len(curves) 515*e1fe3e4aSElliott Hughes 516*e1fe3e4aSElliott Hughes l = len(curves) 517*e1fe3e4aSElliott Hughes splines = [None] * l 518*e1fe3e4aSElliott Hughes last_i = i = 0 519*e1fe3e4aSElliott Hughes n = 1 520*e1fe3e4aSElliott Hughes while True: 521*e1fe3e4aSElliott Hughes spline = cubic_approx_spline(curves[i], n, max_errors[i], all_quadratic) 522*e1fe3e4aSElliott Hughes if spline is None: 523*e1fe3e4aSElliott Hughes if n == MAX_N: 524*e1fe3e4aSElliott Hughes break 525*e1fe3e4aSElliott Hughes n += 1 526*e1fe3e4aSElliott Hughes last_i = i 527*e1fe3e4aSElliott Hughes continue 528*e1fe3e4aSElliott Hughes splines[i] = spline 529*e1fe3e4aSElliott Hughes i = (i + 1) % l 530*e1fe3e4aSElliott Hughes if i == last_i: 531*e1fe3e4aSElliott Hughes # done. go home 532*e1fe3e4aSElliott Hughes return [[(s.real, s.imag) for s in spline] for spline in splines] 533*e1fe3e4aSElliott Hughes 534*e1fe3e4aSElliott Hughes raise ApproxNotFoundError(curves) 535