Coverage for src/ensae_teaching_cs/special/geometry_point.py: 63%
102 statements
« prev ^ index » next coverage.py v7.1.0, created at 2023-04-28 06:23 +0200
« prev ^ index » next coverage.py v7.1.0, created at 2023-04-28 06:23 +0200
1"""
2@file
3@brief Defines a point in N-dimension
4"""
6import math
9class GeometryException(Exception):
10 """
11 raises when an issue arises with class GeometryPoint
12 """
13 pass
16class GeometryPoint:
17 """
18 one point
19 """
20 __slots__ = ["_x"]
22 def __init__(self, *x):
23 """
24 @param x is a vector
25 """
26 if isinstance(x, (tuple, list)):
27 if len(x) == 0:
28 raise ValueError("empty dimension")
29 if isinstance(x[0], GeometryPoint) and len(x) == 1:
30 self._x = x[0]._x
31 else:
32 self._x = tuple(x)
33 else:
34 raise TypeError(f"Unexpected type {type(x)!r}.")
36 def __eq__(self, x):
37 """
38 is equal
39 """
40 return self._x == x
42 def __neq__(self, x):
43 """
44 is different
45 """
46 return not self.__eq__(x)
48 def __len__(self):
49 """
50 returns the dimension
51 """
52 return len(self._x)
54 def __str__(self):
55 """
56 converts into string
57 """
58 if len(self) == 2:
59 s = "({0},{1})".format(*self._x)
60 return s.replace(".000000", "")
62 format = ", ".join(["{}" for _ in self._x])
63 t = format.format(*self._x)
64 s = f"({t})"
65 return s.replace(".000000", "")
67 def __repr__(self):
68 """
69 ``eval(__repr__)`` should return the same object
70 """
71 return f"GeometryPoint({', '.join(str(_) for _ in self._x)})"
73 def __iadd__(self, x):
74 """
75 addition
76 """
77 if len(self) != len(x):
78 raise GeometryException(
79 f"dimension problem {len(self)} != {len(x)}")
80 if len(self) == 2:
81 self._x = (self._x[0] + x._x[0], self._x[1] + x._x[1])
82 else:
83 self._x = tuple(a + b for a, b in zip(self._x, x._x))
84 return self
86 def __add__(self, x):
87 """
88 addition
89 """
90 if len(self) != len(x):
91 raise GeometryException(
92 f"dimension problem {len(self)} != {len(x)}")
93 if len(self) == 2:
94 return GeometryPoint(self._x[0] + x._x[0], self._x[1] + x._x[1])
95 else:
96 return GeometryPoint(a + b for a, b in zip(self._x, x._x))
98 def __sub__(self, x):
99 """
100 substraction
101 """
102 if len(self) != len(x):
103 raise GeometryException(
104 f"dimension problem {len(self)} != {len(x)}")
105 if len(self) == 2:
106 return GeometryPoint(self._x[0] - x._x[0], self._x[1] - x._x[1])
107 return GeometryPoint(a - b for a, b in zip(self._x, x._x))
109 def __imul__(self, k):
110 """
111 multiplication by a scalar
112 """
113 if len(self) == 2:
114 self._x = (self._x[0] * k, self._x[1] * k)
115 else:
116 self._x = tuple(_ * k for _ in self._x)
117 return self
119 def __mul__(self, k):
120 """
121 multiplication by a scalar
122 """
123 if len(self) == 2:
124 return GeometryPoint(self._x[0] * k, self._x[1] * k)
125 else:
126 return GeometryPoint(_ * k for _ in self._x)
128 def scalar(self, x):
129 """
130 scalar product
131 """
132 if len(self) != len(x):
133 raise GeometryException("dimension problem %d != %d\n%s ? %s" % (len(self), len(x),
134 str(self), str(x)))
135 r = 0.
136 for a, b in zip(self._x, x._x):
137 r += a * b
138 return r
140 def __cmp__(self, x):
141 """
142 comparison
143 """
144 if len(self) != len(x):
145 raise GeometryException(
146 f"dimension problem {len(self)} != {len(x)}")
147 for a, b in zip(self._x, x._x):
148 t = -1 if a < b else (0 if a == b else 1)
149 if t != 0:
150 return t
151 return 0
153 def __lt__(self, x):
154 """
155 inferior
156 """
157 return self.__cmp__(x) == -1
159 def product(self, x):
160 """
161 vectoriel product, dimension 2 only
162 """
163 if len(self) != 2:
164 raise GeometryException(
165 "this function only exists if len(self) == 2")
167 return self._x[1] * x._x[0] - self._x[0] * x._x[1]
169 def cossin(self):
170 """
171 return the cos, sin of a vector (dimension 2 only)
172 """
173 n = self.norm2()
174 if n == 0.:
175 return 1., 0.
176 n = n ** 0.5
177 p = GeometryPoint(1., 0.)
178 cos = self.scalar(p) / n
179 sin = self.product(p) / n
180 return cos, sin
182 def norm2(self):
183 """
184 return the norm
185 """
186 return self.scalar(self)
188 def angle(self):
189 """
190 return the angle
191 """
192 cos, sin = self.cossin()
193 if cos == 0:
194 if sin == 0:
195 return 0
196 elif sin > 0:
197 return math.pi / 2
198 else:
199 return -math.pi / 2
200 else:
201 t = sin / cos
202 a = math.atan(t)
203 if cos < 0:
204 return a - math.pi
205 else:
206 return a