Coverage for src/ensae_teaching_cs/special/geometry_point.py: 63%

102 statements  

« prev     ^ index     » next       coverage.py v7.1.0, created at 2023-01-27 05:44 +0100

1""" 

2@file 

3@brief Defines a point in N-dimension 

4""" 

5 

6import math 

7 

8 

9class GeometryException(Exception): 

10 """ 

11 raises when an issue arises with class GeometryPoint 

12 """ 

13 pass 

14 

15 

16class GeometryPoint: 

17 """ 

18 one point 

19 """ 

20 __slots__ = ["_x"] 

21 

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}.") 

35 

36 def __eq__(self, x): 

37 """ 

38 is equal 

39 """ 

40 return self._x == x 

41 

42 def __neq__(self, x): 

43 """ 

44 is different 

45 """ 

46 return not self.__eq__(x) 

47 

48 def __len__(self): 

49 """ 

50 returns the dimension 

51 """ 

52 return len(self._x) 

53 

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", "") 

61 

62 format = ", ".join(["{}" for _ in self._x]) 

63 t = format.format(*self._x) 

64 s = f"({t})" 

65 return s.replace(".000000", "") 

66 

67 def __repr__(self): 

68 """ 

69 ``eval(__repr__)`` should return the same object 

70 """ 

71 return f"GeometryPoint({', '.join(str(_) for _ in self._x)})" 

72 

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 

85 

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)) 

97 

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)) 

108 

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 

118 

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) 

127 

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 

139 

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 

152 

153 def __lt__(self, x): 

154 """ 

155 inferior 

156 """ 

157 return self.__cmp__(x) == -1 

158 

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") 

166 

167 return self._x[1] * x._x[0] - self._x[0] * x._x[1] 

168 

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 

181 

182 def norm2(self): 

183 """ 

184 return the norm 

185 """ 

186 return self.scalar(self) 

187 

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