"""Mitre Glyph: 

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

"""

from FL import *
import math

def getContours(g):
    nLength = len(g.nodes)
    contours = []
    cid = -1
    for i in range(nLength):
        n = g.nodes[i]
        if n.type == nMOVE:
            cid += 1
            contours.append([])
        contours[cid].append(n)
    return contours

def getTangents(contours):
    tmap = []
    for c in contours:
        clen = len(c)
        for i in range(clen):
            n = c[i]
            p = Point(n.x, n.y)
            nn = c[(i + 1) % clen]
            pn = c[(clen + i - 1) % clen]
            if nn.type == nCURVE:
                np = Point(nn[1].x,nn[1].y)
            else:
                np = Point(nn.x,nn.y)    
            if n.type == nCURVE:
                pp = Point(n[2].x,n[2].y)
            else:
                pp = Point(pn.x,pn.y)
            nVect = Point(-p.x + np.x, -p.y + np.y)
            pVect = Point(-p.x + pp.x, -p.y + pp.y)
            tmap.append((pVect,nVect))
    return tmap    

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

def getMagnitude(p):
    return math.sqrt(p.x*p.x + p.y*p.y)
    
def getDistance(v1,v2):
    return getMagnitude(Point(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 = Point(round(v1.x * radius), round(v1.y * radius))
    offset2 = Point(round(v2.x * radius), round(v2.y * radius))
    return offset1, offset2

def mitreGlyph(g,mitreSize,maxAngle):
    if g == None:
        return
    
    contours = getContours(g)
    tangents = getTangents(contours)
    nodes = []
    needsMitring = False
    nid = -1
    for c in contours:
        for n in c:
            nid += 1
            v1, v2 = tangents[nid]
            off = getMitreOffset(n,v1,v2,mitreSize,maxAngle)
            n1 = Node(n)
            if off != None:
                offset1, offset2 = off
                n2 = Node(nLINE, Point(n.x + offset2.x, n.y + offset2.y))
                n1[0].x += offset1.x
                n1[0].y += offset1.y
                nodes.append(n1)
                nodes.append(n2)
                needsMitring = True
            else:
                nodes.append(n1)
    if needsMitring:
        g.Clear()
        g.Insert(nodes)
    
fl.SetUndo()
mitreGlyph(fl.glyph,8.,.9)
fl.UpdateGlyph()