aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/fonttools/fontTools/misc/vector.py
blob: 02c62e6512a04f3497f7c9987a1f414b30cf6b05 (plain) (blame)
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
from numbers import Number
import math
import operator
import warnings


__all__ = ["Vector"]


class Vector(tuple):
    """A math-like vector.

    Represents an n-dimensional numeric vector. ``Vector`` objects support
    vector addition and subtraction, scalar multiplication and division,
    negation, rounding, and comparison tests.
    """

    __slots__ = ()

    def __new__(cls, values, keep=False):
        if keep is not False:
            warnings.warn(
                "the 'keep' argument has been deprecated",
                DeprecationWarning,
            )
        if type(values) == Vector:
            # No need to create a new object
            return values
        return super().__new__(cls, values)

    def __repr__(self):
        return f"{self.__class__.__name__}({super().__repr__()})"

    def _vectorOp(self, other, op):
        if isinstance(other, Vector):
            assert len(self) == len(other)
            return self.__class__(op(a, b) for a, b in zip(self, other))
        if isinstance(other, Number):
            return self.__class__(op(v, other) for v in self)
        raise NotImplementedError()

    def _scalarOp(self, other, op):
        if isinstance(other, Number):
            return self.__class__(op(v, other) for v in self)
        raise NotImplementedError()

    def _unaryOp(self, op):
        return self.__class__(op(v) for v in self)

    def __add__(self, other):
        return self._vectorOp(other, operator.add)

    __radd__ = __add__

    def __sub__(self, other):
        return self._vectorOp(other, operator.sub)

    def __rsub__(self, other):
        return self._vectorOp(other, _operator_rsub)

    def __mul__(self, other):
        return self._scalarOp(other, operator.mul)

    __rmul__ = __mul__

    def __truediv__(self, other):
        return self._scalarOp(other, operator.truediv)

    def __rtruediv__(self, other):
        return self._scalarOp(other, _operator_rtruediv)

    def __pos__(self):
        return self._unaryOp(operator.pos)

    def __neg__(self):
        return self._unaryOp(operator.neg)

    def __round__(self, *, round=round):
        return self._unaryOp(round)

    def __eq__(self, other):
        if isinstance(other, list):
            # bw compat Vector([1, 2, 3]) == [1, 2, 3]
            other = tuple(other)
        return super().__eq__(other)

    def __ne__(self, other):
        return not self.__eq__(other)

    def __bool__(self):
        return any(self)

    __nonzero__ = __bool__

    def __abs__(self):
        return math.sqrt(sum(x * x for x in self))

    def length(self):
        """Return the length of the vector. Equivalent to abs(vector)."""
        return abs(self)

    def normalized(self):
        """Return the normalized vector of the vector."""
        return self / abs(self)

    def dot(self, other):
        """Performs vector dot product, returning the sum of
        ``a[0] * b[0], a[1] * b[1], ...``"""
        assert len(self) == len(other)
        return sum(a * b for a, b in zip(self, other))

    # Deprecated methods/properties

    def toInt(self):
        warnings.warn(
            "the 'toInt' method has been deprecated, use round(vector) instead",
            DeprecationWarning,
        )
        return self.__round__()

    @property
    def values(self):
        warnings.warn(
            "the 'values' attribute has been deprecated, use "
            "the vector object itself instead",
            DeprecationWarning,
        )
        return list(self)

    @values.setter
    def values(self, values):
        raise AttributeError(
            "can't set attribute, the 'values' attribute has been deprecated",
        )

    def isclose(self, other: "Vector", **kwargs) -> bool:
        """Return True if the vector is close to another Vector."""
        assert len(self) == len(other)
        return all(math.isclose(a, b, **kwargs) for a, b in zip(self, other))


def _operator_rsub(a, b):
    return operator.sub(b, a)


def _operator_rtruediv(a, b):
    return operator.truediv(b, a)