This repository has been archived on 2025-10-02. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
inter-font/misc/pylib/fontbuild/convertCurves.pyx
Rasmus Andersson 8234b62ab7 Speeds up font compilation by around 200%
Cython is used to compile some hot paths into native Python extensions.
These hot paths were identified through running ufocompile with the hotshot
profiler and then converting file by file to Cython, starting with the "hottest"
paths and continuing until returns were deminishing. This means that only a few
Python files were converted to Cython.

Closes #23
Closes #20 (really this time)
2017-09-04 11:12:34 -04:00

102 lines
3.4 KiB
Cython

#! /usr/bin/env python
#
# Copyright 2015 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Converts a cubic bezier curve to a quadratic spline with
exactly two off curve points.
"""
import numpy
from numpy import array,cross,dot
from fontTools.misc import bezierTools
from robofab.objects.objectsRF import RSegment
def replaceSegments(contour, segments):
while len(contour):
contour.removeSegment(0)
for s in segments:
contour.appendSegment(s.type, [(p.x, p.y) for p in s.points], s.smooth)
def calcIntersect(a,b,c,d):
numpy.seterr(all='raise')
e = b-a
f = d-c
p = array([-e[1], e[0]])
try:
h = dot((a-c),p) / dot(f,p)
except:
print a,b,c,d
raise
return c + dot(f,h)
def simpleConvertToQuadratic(p0,p1,p2,p3):
p = [array(i.x,i.y) for i in [p0,p1,p2,p3]]
off = calcIntersect(p[0],p[1],p[2],p[3])
# OFFCURVE_VECTOR_CORRECTION = -.015
OFFCURVE_VECTOR_CORRECTION = 0
def convertToQuadratic(p0,p1,p2,p3):
# TODO: test for accuracy and subdivide further if needed
p = [(i.x,i.y) for i in [p0,p1,p2,p3]]
# if p[0][0] == p[1][0] and p[0][0] == p[2][0] and p[0][0] == p[2][0] and p[0][0] == p[3][0]:
# return (p[0],p[1],p[2],p[3])
# if p[0][1] == p[1][1] and p[0][1] == p[2][1] and p[0][1] == p[2][1] and p[0][1] == p[3][1]:
# return (p[0],p[1],p[2],p[3])
seg1,seg2 = bezierTools.splitCubicAtT(p[0], p[1], p[2], p[3], .5)
pts1 = [array([i[0], i[1]]) for i in seg1]
pts2 = [array([i[0], i[1]]) for i in seg2]
on1 = seg1[0]
on2 = seg2[3]
try:
off1 = calcIntersect(pts1[0], pts1[1], pts1[2], pts1[3])
off2 = calcIntersect(pts2[0], pts2[1], pts2[2], pts2[3])
except:
return (p[0],p[1],p[2],p[3])
off1 = (on1 - off1) * OFFCURVE_VECTOR_CORRECTION + off1
off2 = (on2 - off2) * OFFCURVE_VECTOR_CORRECTION + off2
return (on1,off1,off2,on2)
def cubicSegmentToQuadratic(c,sid):
segment = c[sid]
if (segment.type != "curve"):
print "Segment type not curve"
return
#pSegment,junk = getPrevAnchor(c,sid)
pSegment = c[sid-1] #assumes that a curve type will always be proceeded by another point on the same contour
points = convertToQuadratic(pSegment.points[-1],segment.points[0],
segment.points[1],segment.points[2])
return RSegment(
'qcurve', [[int(i) for i in p] for p in points[1:]], segment.smooth)
def glyphCurvesToQuadratic(g):
for c in g:
segments = []
for i in range(len(c)):
s = c[i]
if s.type == "curve":
try:
segments.append(cubicSegmentToQuadratic(c, i))
except Exception:
print g.name, i
raise
else:
segments.append(s)
replaceSegments(c, segments)