summaryrefslogtreecommitdiff
path: root/scripts/lib/fontbuild/mitreGlyph.py
blob: 0ee14485a54517b7e4cbc63a6ac7a0b52c4dd9cf (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
"""Mitre Glyph: 

mitreSize : Length of the segment created by the mitre. The default is 4.
maxAngle :  Maximum angle in radians at which segments will be mitred. The default is .9 (about 50 degrees).
            Works for both inside and outside angles

"""

import math
from robofab.objects.objectsRF import RPoint, RSegment
from fontbuild.convertCurves import replaceSegments

def getTangents(contours):
    tmap = []
    for c in contours:
        clen = len(c)
        for i in range(clen):
            s = c[i]
            p = s.points[-1]
            ns = c[(i + 1) % clen]
            ps = c[(clen + i - 1) % clen]
            np = ns.points[1] if ns.type == 'curve' else ns.points[-1]
            pp = s.points[2] if s.type == 'curve' else ps.points[-1]
            tmap.append((pp - p, np - p))
    return tmap    

def normalizeVector(p):
    m = getMagnitude(p);
    if m != 0:
        return p*(1/m)
    else:
        return RPoint(0,0)

def getMagnitude(p):
    return math.sqrt(p.x*p.x + p.y*p.y)
    
def getDistance(v1,v2):
    return getMagnitude(RPoint(v1.x - v2.x, v1.y - v2.y))

def getAngle(v1,v2):
    angle = math.atan2(v1.y,v1.x) - math.atan2(v2.y,v2.x)
    return (angle + (2*math.pi)) % (2*math.pi)
    
def angleDiff(a,b):
    return math.pi - abs((abs(a - b) % (math.pi*2)) - math.pi)

def getAngle2(v1,v2):
    return abs(angleDiff(math.atan2(v1.y, v1.x), math.atan2(v2.y, v2.x)))

def getMitreOffset(n,v1,v2,mitreSize=4,maxAngle=.9):
    
    # dont mitre if segment is too short
    if abs(getMagnitude(v1)) < mitreSize * 2 or abs(getMagnitude(v2)) < mitreSize * 2:
        return
    angle = getAngle2(v2,v1)
    v1 = normalizeVector(v1)
    v2 = normalizeVector(v2)
    if v1.x == v2.x and v1.y == v2.y:
        return
    
    
    # only mitre corners sharper than maxAngle
    if angle > maxAngle:
        return
    
    radius = mitreSize / abs(getDistance(v1,v2))
    offset1 = RPoint(round(v1.x * radius), round(v1.y * radius))
    offset2 = RPoint(round(v2.x * radius), round(v2.y * radius))
    return offset1, offset2

def mitreGlyph(g,mitreSize,maxAngle):
    if g == None:
        return
    
    tangents = getTangents(g.contours)
    sid = -1
    for c in g.contours:
        segments = []
        needsMitring = False
        for s in c:
            sid += 1
            v1, v2 = tangents[sid]
            off = getMitreOffset(s,v1,v2,mitreSize,maxAngle)
            s1 = s.copy()
            if off != None:
                offset1, offset2 = off
                p2 = s.points[-1] + offset2
                s2 = RSegment('line', [(p2.x, p2.y)])
                s1.points[0] += offset1
                segments.append(s1)
                segments.append(s2)
                needsMitring = True
            else:
                segments.append(s1)
        if needsMitring:
            replaceSegments(c, segments)