some refactoring, some doctests

git-svn-id: svn://svn.code.sf.net/p/fonttools/code/trunk@487 4cde692c-a291-49d1-8350-778aa11640f8
This commit is contained in:
jvr 2005-02-25 10:11:30 +00:00
parent cecd58c31c
commit 8ee2561bc1

View File

@ -11,15 +11,14 @@ import Numeric
def calcQuadraticBounds(pt1, pt2, pt3):
"""Return the bounding rectangle for a qudratic bezier segment.
pt1 and pt3 are the "anchor" points, pt2 is the "handle"."""
# convert points to Numeric arrays
pt1, pt2, pt3 = Numeric.array((pt1, pt2, pt3))
# calc quadratic parameters
c = pt1
b = (pt2 - c) * 2.0
a = pt3 - c - b
pt1 and pt3 are the "anchor" points, pt2 is the "handle".
>>> calcQuadraticBounds((0, 0), (50, 100), (100, 0))
(0.0, 0.0, 100.0, 50.0)
>>> calcQuadraticBounds((0, 0), (100, 0), (100, 100))
(0.0, 0.0, 100.0, 100.0)
"""
a, b, c = calcQuadraticParameters(pt1, pt2, pt3)
# calc first derivative
ax, ay = a * 2
bx, by = b
@ -34,16 +33,16 @@ def calcQuadraticBounds(pt1, pt2, pt3):
def calcCubicBounds(pt1, pt2, pt3, pt4):
"""Return the bounding rectangle for a cubic bezier segment.
pt1 and pt4 are the "anchor" points, pt2 and pt3 are the "handles"."""
# convert points to Numeric arrays
pt1, pt2, pt3, pt4 = Numeric.array((pt1, pt2, pt3, pt4))
# calc cubic parameters
d = pt1
c = (pt2 - d) * 3.0
b = (pt3 - pt2) * 3.0 - c
a = pt4 - d - c - b
pt1 and pt4 are the "anchor" points, pt2 and pt3 are the "handles".
>>> calcCubicBounds((0, 0), (25, 100), (75, 100), (100, 0))
(0.0, 0.0, 100.0, 75.0)
>>> calcCubicBounds((0, 0), (50, 0), (100, 50), (100, 100))
(0.0, 0.0, 100.0, 100.0)
>>> calcCubicBounds((50, 0), (0, 100), (100, 100), (50, 0))
(35.566243270259356, 0.0, 64.433756729740679, 75.0)
"""
a, b, c, d = calcCubicParameters(pt1, pt2, pt3, pt4)
# calc first derivative
ax, ay = a * 3.0
bx, by = b * 2.0
@ -61,7 +60,17 @@ def splitLine(pt1, pt2, where, isHorizontal):
is an x coordinate if isHorizontal is False, a y coordinate if
isHorizontal is True. Return a list of two line segments if the
line was successfully split, or a list containing the original
line."""
line.
>>> _tuplify(splitLine((0, 0), (100, 100), 50, True))
(((0, 0), (50.0, 50.0)), ((50.0, 50.0), (100, 100)))
>>> _tuplify(splitLine((0, 0), (100, 100), 100, True))
(((0, 0), (100, 100)),)
>>> _tuplify(splitLine((0, 0), (100, 100), 0, True))
(((0, 0), (0.0, 0.0)), ((0.0, 0.0), (100, 100)))
>>> _tuplify(splitLine((0, 0), (100, 100), 0, False))
(((0, 0), (0.0, 0.0)), ((0.0, 0.0), (100, 100)))
"""
pt1, pt2 = Numeric.array((pt1, pt2))
a = (pt2 - pt1)
b = pt1
@ -79,11 +88,21 @@ def splitLine(pt1, pt2, where, isHorizontal):
def splitQuadratic(pt1, pt2, pt3, where, isHorizontal):
"""Split the quadratic curve between pt1, pt2 and pt3 at position 'where',
which is an x coordinate if isHorizontal is False, a y coordinate if
isHorizontal is True. Return a list of curve segments."""
pt1, pt2, pt3 = Numeric.array((pt1, pt2, pt3))
c = pt1
b = (pt2 - c) * 2.0
a = pt3 - c - b
isHorizontal is True. Return a list of curve segments.
>>> splitQuadratic((0, 0), (50, 100), (100, 0), 150, False)
[((0, 0), (50, 100), (100, 0))]
>>> _tuplify(splitQuadratic((0, 0), (50, 100), (100, 0), 50, False))
(((0.0, 0.0), (25.0, 50.0), (50.0, 50.0)), ((50.0, 50.0), (75.0, 50.0), (100.0, 0.0)))
>>> _tuplify(splitQuadratic((0, 0), (50, 100), (100, 0), 25, False))
(((0.0, 0.0), (12.5, 25.0), (25.0, 37.5)), ((25.0, 37.5), (62.5, 75.0), (100.0, 0.0)))
>>> _tuplify(splitQuadratic((0, 0), (50, 100), (100, 0), 25, True))
(((0.0, 0.0), (7.3223304703363103, 14.644660940672621), (14.644660940672621, 24.999999999999996)), ((14.644660940672621, 24.999999999999996), (49.999999999999993, 75.0), (85.355339059327363, 25.000000000000025)), ((85.355339059327378, 25.0), (92.677669529663689, 14.644660940672621), (100.0, -7.1054273576010019e-15)))
>>> # XXX I'm not at all sure it the following behavior is desirable:
>>> _tuplify(splitQuadratic((0, 0), (50, 100), (100, 0), 50, True))
(((0.0, 0.0), (25.0, 50.0), (50.0, 50.0)), ((50.0, 50.0), (50.0, 50.0), (50.0, 50.0)), ((50.0, 50.0), (75.0, 50.0), (100.0, 0.0)))
"""
a, b, c = calcQuadraticParameters(pt1, pt2, pt3)
solutions = solveQuadratic(a[isHorizontal], b[isHorizontal],
c[isHorizontal] - where)
solutions = [t for t in solutions if 0 <= t < 1]
@ -114,12 +133,7 @@ def splitCubic(pt1, pt2, pt3, pt4, where, isHorizontal):
"""Split the cubic curve between pt1, pt2, pt3 and pt4 at position 'where',
which is an x coordinate if isHorizontal is False, a y coordinate if
isHorizontal is True. Return a list of curve segments."""
pt1, pt2, pt3, pt4 = Numeric.array((pt1, pt2, pt3, pt4))
d = pt1
c = (pt2 - d) * 3.0
b = (pt3 - pt2) * 3.0 - c
a = pt4 - d - c - b
a, b, c, d = calcCubicParameters(pt1, pt2, pt3, pt4)
solutions = solveCubic(a[isHorizontal], b[isHorizontal], c[isHorizontal],
d[isHorizontal] - where)
solutions = [t for t in solutions if 0 <= t < 1]
@ -224,3 +238,38 @@ def solveCubic(a, b, c, d,
x = -x
x = x - a1/3.0
return [x]
def calcQuadraticParameters(pt1, pt2, pt3):
pt1, pt2, pt3 = Numeric.array((pt1, pt2, pt3))
c = pt1
b = (pt2 - c) * 2.0
a = pt3 - c - b
return a, b, c
def calcCubicParameters(pt1, pt2, pt3, pt4):
pt1, pt2, pt3, pt4 = Numeric.array((pt1, pt2, pt3, pt4))
d = pt1
c = (pt2 - d) * 3.0
b = (pt3 - pt2) * 3.0 - c
a = pt4 - d - c - b
return a, b, c, d
def _tuplify(obj):
"""
>>> _tuplify([1, [2, 3], [], [[2, [3, 4]]]])
(1, (2, 3), (), ((2, (3, 4)),))
"""
try:
it = iter(obj)
except TypeError:
return obj
else:
return tuple([_tuplify(x) for x in it])
if __name__ == "__main__":
import doctest
doctest.testmod()