summaryrefslogtreecommitdiff
path: root/scripts/lib/fontbuild/convertCurves.py
blob: b6efd5ca2f47a7cec56fe2baeaa197ec8b2b976a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#! /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)