aboutsummaryrefslogtreecommitdiff
path: root/Lib/fontTools/misc/arrayTools.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/fontTools/misc/arrayTools.py')
-rw-r--r--Lib/fontTools/misc/arrayTools.py65
1 files changed, 55 insertions, 10 deletions
diff --git a/Lib/fontTools/misc/arrayTools.py b/Lib/fontTools/misc/arrayTools.py
index 01ccbe82..ced8d87a 100644
--- a/Lib/fontTools/misc/arrayTools.py
+++ b/Lib/fontTools/misc/arrayTools.py
@@ -23,6 +23,7 @@ def calcBounds(array):
ys = [y for x, y in array]
return min(xs), min(ys), max(xs), max(ys)
+
def calcIntBounds(array, round=otRound):
"""Calculate the integer bounding rectangle of a 2D points array.
@@ -46,7 +47,7 @@ def updateBounds(bounds, p, min=min, max=max):
Args:
bounds: A bounding rectangle expressed as a tuple
- ``(xMin, yMin, xMax, yMax)``.
+ ``(xMin, yMin, xMax, yMax), or None``.
p: A 2D tuple representing a point.
min,max: functions to compute the minimum and maximum.
@@ -54,9 +55,12 @@ def updateBounds(bounds, p, min=min, max=max):
The updated bounding rectangle ``(xMin, yMin, xMax, yMax)``.
"""
(x, y) = p
+ if bounds is None:
+ return x, y, x, y
xMin, yMin, xMax, yMax = bounds
return min(xMin, x), min(yMin, y), max(xMax, x), max(yMax, y)
+
def pointInRect(p, rect):
"""Test if a point is inside a bounding rectangle.
@@ -72,6 +76,7 @@ def pointInRect(p, rect):
xMin, yMin, xMax, yMax = rect
return (xMin <= x <= xMax) and (yMin <= y <= yMax)
+
def pointsInRect(array, rect):
"""Determine which points are inside a bounding rectangle.
@@ -88,6 +93,7 @@ def pointsInRect(array, rect):
xMin, yMin, xMax, yMax = rect
return [(xMin <= x <= xMax) and (yMin <= y <= yMax) for x, y in array]
+
def vectorLength(vector):
"""Calculate the length of the given vector.
@@ -100,6 +106,7 @@ def vectorLength(vector):
x, y = vector
return math.sqrt(x**2 + y**2)
+
def asInt16(array):
"""Round a list of floats to 16-bit signed integers.
@@ -109,7 +116,7 @@ def asInt16(array):
Returns:
A list of rounded integers.
"""
- return [int(math.floor(i+0.5)) for i in array]
+ return [int(math.floor(i + 0.5)) for i in array]
def normRect(rect):
@@ -130,6 +137,7 @@ def normRect(rect):
(xMin, yMin, xMax, yMax) = rect
return min(xMin, xMax), min(yMin, yMax), max(xMin, xMax), max(yMin, yMax)
+
def scaleRect(rect, x, y):
"""Scale a bounding box rectangle.
@@ -145,6 +153,7 @@ def scaleRect(rect, x, y):
(xMin, yMin, xMax, yMax) = rect
return xMin * x, yMin * y, xMax * x, yMax * y
+
def offsetRect(rect, dx, dy):
"""Offset a bounding box rectangle.
@@ -158,7 +167,8 @@ def offsetRect(rect, dx, dy):
An offset bounding rectangle.
"""
(xMin, yMin, xMax, yMax) = rect
- return xMin+dx, yMin+dy, xMax+dx, yMax+dy
+ return xMin + dx, yMin + dy, xMax + dx, yMax + dy
+
def insetRect(rect, dx, dy):
"""Inset a bounding box rectangle on all sides.
@@ -173,7 +183,8 @@ def insetRect(rect, dx, dy):
An inset bounding rectangle.
"""
(xMin, yMin, xMax, yMax) = rect
- return xMin+dx, yMin+dy, xMax-dx, yMax-dy
+ return xMin + dx, yMin + dy, xMax - dx, yMax - dy
+
def sectRect(rect1, rect2):
"""Test for rectangle-rectangle intersection.
@@ -191,12 +202,17 @@ def sectRect(rect1, rect2):
"""
(xMin1, yMin1, xMax1, yMax1) = rect1
(xMin2, yMin2, xMax2, yMax2) = rect2
- xMin, yMin, xMax, yMax = (max(xMin1, xMin2), max(yMin1, yMin2),
- min(xMax1, xMax2), min(yMax1, yMax2))
+ xMin, yMin, xMax, yMax = (
+ max(xMin1, xMin2),
+ max(yMin1, yMin2),
+ min(xMax1, xMax2),
+ min(yMax1, yMax2),
+ )
if xMin >= xMax or yMin >= yMax:
return False, (0, 0, 0, 0)
return True, (xMin, yMin, xMax, yMax)
+
def unionRect(rect1, rect2):
"""Determine union of bounding rectangles.
@@ -211,10 +227,15 @@ def unionRect(rect1, rect2):
"""
(xMin1, yMin1, xMax1, yMax1) = rect1
(xMin2, yMin2, xMax2, yMax2) = rect2
- xMin, yMin, xMax, yMax = (min(xMin1, xMin2), min(yMin1, yMin2),
- max(xMax1, xMax2), max(yMax1, yMax2))
+ xMin, yMin, xMax, yMax = (
+ min(xMin1, xMin2),
+ min(yMin1, yMin2),
+ max(xMax1, xMax2),
+ max(yMax1, yMax2),
+ )
return (xMin, yMin, xMax, yMax)
+
def rectCenter(rect):
"""Determine rectangle center.
@@ -226,7 +247,8 @@ def rectCenter(rect):
A 2D tuple representing the point at the center of the rectangle.
"""
(xMin, yMin, xMax, yMax) = rect
- return (xMin+xMax)/2, (yMin+yMax)/2
+ return (xMin + xMax) / 2, (yMin + yMax) / 2
+
def rectArea(rect):
"""Determine rectangle area.
@@ -241,6 +263,7 @@ def rectArea(rect):
(xMin, yMin, xMax, yMax) = rect
return (yMax - yMin) * (xMax - xMin)
+
def intRect(rect):
"""Round a rectangle to integer values.
@@ -261,8 +284,28 @@ def intRect(rect):
return (xMin, yMin, xMax, yMax)
-class Vector(_Vector):
+def quantizeRect(rect, factor=1):
+ """
+ >>> bounds = (72.3, -218.4, 1201.3, 919.1)
+ >>> quantizeRect(bounds)
+ (72, -219, 1202, 920)
+ >>> quantizeRect(bounds, factor=10)
+ (70, -220, 1210, 920)
+ >>> quantizeRect(bounds, factor=100)
+ (0, -300, 1300, 1000)
+ """
+ if factor < 1:
+ raise ValueError(f"Expected quantization factor >= 1, found: {factor!r}")
+ xMin, yMin, xMax, yMax = normRect(rect)
+ return (
+ int(math.floor(xMin / factor) * factor),
+ int(math.floor(yMin / factor) * factor),
+ int(math.ceil(xMax / factor) * factor),
+ int(math.ceil(yMax / factor) * factor),
+ )
+
+class Vector(_Vector):
def __init__(self, *args, **kwargs):
warnings.warn(
"fontTools.misc.arrayTools.Vector has been deprecated, please use "
@@ -373,7 +416,9 @@ def _test():
(0, 2, 4, 5)
"""
+
if __name__ == "__main__":
import sys
import doctest
+
sys.exit(doctest.testmod().failed)