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/robofab/path/intersect.py
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

108 lines
3.1 KiB
Python

from robofab.pens.filterPen import flattenGlyph
from robofab.objects.objectsRF import RGlyph as _RGlyph
import math
_EPSILON = 1e-15
def normalise(a1, a2):
"""Normalise this vector to length 1"""
n = math.sqrt((a1*a1)+(a2*a2))
return (a1/n, a2/n)
def inbetween((a1, a2), (b1, b2), (c1, c2)):
"""Return True if point b is in between points a and c."""
x = (a1-_EPSILON<=b1<=c1+_EPSILON) or (a1+_EPSILON>=b1>=c1-_EPSILON)
y = (a2-_EPSILON<=b2<=c2+_EPSILON) or (a2+_EPSILON>=b2>=c2-_EPSILON)
return x == y == True
def sectlines((a1, a2), (p1, p2), (b1, b2), (q1, q2)):
'''Calculate the intersection point of two straight lines. Result in floats.'''
if (a1, a2) == (p1, p2):
return None
r1 = a1-p1
r2 = a2-p2
r1, r2 = normalise(r1, r2)
s1 = b1-q1
s2 = b2-q2
s1, s2 = normalise(s1, s2)
f = float(s1*r2 - s2*r1)
if f == 0:
return None
mu = (r1*(q2 - p2) + r2*(p1 - q1)) / f
m1 = mu*s1 + q1
m2 = mu*s2 + q2
if (m1, m2) == (a1, a2):
return None
if inbetween((a1, a2), (m1, m2), (p1,p2)) and inbetween((b1, b2), (m1, m2), (q1,q2)):
return m1, m2
else:
return None
def _makeFlat(aGlyph, segmentLength = 10):
"""Helper function to flatten the glyph with a given approximate segment length."""
return flattenGlyph(aGlyph, segmentLength)
def intersect(aGlyph, startPt, endPt, segmentLength=10):
"""Find the intersections between a glyph and a straight line."""
flat = _makeFlat(aGlyph)
return _intersect(flat, startPt, endPt, segmentLength)
def _intersect(flat, startPt, endPt, segmentLength=10):
"""Find the intersections between a flattened glyph and a straight line."""
if len(flat.contours) == 0:
return None
if startPt == endPt:
return None
sect = None
intersections = {}
# new contains the flattened outline
for c in flat:
l =len(c.points)
for i in range(l):
cur = c.points[i]
next = c.points[(i+1)%l]
sect = sectlines((cur.x, cur.y), (next.x, next.y), startPt, endPt)
if sect is None:
continue
intersections[sect] = 1
return intersections.keys()
def intersectGlyphs(glyphA, glyphB, segmentLength=10):
"""Approximate the intersection points between two glyphs by
flattening both glyphs and checking each tiny segment for
intersections. Slow, but perhaps more realistic then
solving the equasions.
Seems to work for basic curves and straights, but untested
for edges cases, alsmost hits, near hits, double points, crap like that.
"""
flatA = _makeFlat(glyphA)
flatB = _makeFlat(glyphB)
intersections = []
for c in flatA:
l =len(c.points)
for i in range(l):
cur = c.points[i]
next = c.points[(i+1)%l]
sect = _intersect(flatB, (cur.x, cur.y), (next.x, next.y))
if sect is None:
continue
intersections = intersections + sect
return intersections
def makeTestGlyph():
g = _RGlyph()
pen = g.getPen()
pen.moveTo((100, 100))
pen.lineTo((800, 100))
pen.curveTo((1000, 300), (1000, 600), (800, 800))
pen.lineTo((100, 800))
pen.lineTo((100, 100))
pen.closePath()
return g
if __name__ == "__main__":
g = makeTestGlyph()
print intersect(g, (-10, 200), (650, 150))
print intersect(g, (100, 100), (600, 600))