5 from collections
import OrderedDict
7 from .linexprs
import Symbol
17 for symbol
, coordinate
in obj
.coordinates():
18 yield symbol
, func(coordinate
)
20 def _iter2(obj1
, obj2
):
21 if obj1
.symbols
!= obj2
.symbols
:
22 raise ValueError('arguments must belong to the same space')
23 coordinates1
= obj1
._coordinates
.values()
24 coordinates2
= obj2
._coordinates
.values()
25 yield from zip(obj1
.symbols
, coordinates1
, coordinates2
)
27 def _map2(obj1
, obj2
, func
):
28 for symbol
, coordinate1
, coordinate2
in _iter2(obj1
, obj2
):
29 yield symbol
, func(coordinate1
, coordinate2
)
34 This class represents points in space.
37 def __new__(cls
, coordinates
=None):
38 if isinstance(coordinates
, dict):
39 coordinates
= coordinates
.items()
40 self
= object().__new
__(cls
)
41 self
._coordinates
= OrderedDict()
42 for symbol
, coordinate
in sorted(coordinates
,
43 key
=lambda item
: item
[0].sortkey()):
44 if not isinstance(symbol
, Symbol
):
45 raise TypeError('symbols must be Symbol instances')
46 if not isinstance(coordinate
, numbers
.Real
):
47 raise TypeError('coordinates must be real numbers')
48 self
._coordinates
[symbol
] = coordinate
53 return tuple(self
._coordinates
)
57 return len(self
.symbols
)
59 def coordinates(self
):
60 yield from self
._coordinates
.items()
62 def coordinate(self
, symbol
):
63 if not isinstance(symbol
, Symbol
):
64 raise TypeError('symbol must be a Symbol instance')
65 return self
._coordinates
[symbol
]
67 __getitem__
= coordinate
73 return any(self
._coordinates
.values())
75 def __add__(self
, other
):
76 if not isinstance(other
, Vector
):
78 coordinates
= _map2(self
, other
, operator
.add
)
79 return Point(coordinates
)
81 def __sub__(self
, other
):
83 if isinstance(other
, Point
):
84 coordinates
= _map2(self
, other
, operator
.sub
)
85 return Vector(coordinates
)
86 elif isinstance(other
, Vector
):
87 coordinates
= _map2(self
, other
, operator
.sub
)
88 return Point(coordinates
)
92 def __eq__(self
, other
):
93 return isinstance(other
, Point
) and \
94 self
._coordinates
== other
._coordinates
97 return hash(tuple(self
.coordinates()))
100 string
= ', '.join(['{!r}: {!r}'.format(symbol
, coordinate
)
101 for symbol
, coordinate
in self
.coordinates()])
102 return '{}({{{}}})'.format(self
.__class
__.__name
__, string
)
107 This class represents displacements in space.
114 def __new__(cls
, initial
, terminal
=None):
115 self
= object().__new
__(cls
)
116 if not isinstance(initial
, Point
):
117 initial
= Point(initial
)
119 self
._coordinates
= initial
._coordinates
120 elif not isinstance(terminal
, Point
):
121 terminal
= Point(terminal
)
122 self
._coordinates
= _map2(terminal
, initial
, operator
.sub
)
127 return tuple(self
._coordinates
)
131 return len(self
.symbols
)
133 def coordinates(self
):
134 yield from self
._coordinates
.items()
136 def coordinate(self
, symbol
):
137 if not isinstance(symbol
, Symbol
):
138 raise TypeError('symbol must be a Symbol instance')
139 return self
._coordinates
[symbol
]
141 __getitem__
= coordinate
144 return not bool(self
)
147 return any(self
._coordinates
.values())
149 def __add__(self
, other
):
150 if isinstance(other
, (Point
, Vector
)):
151 coordinates
= _map2(self
, other
, operator
.add
)
152 return other
.__class
__(coordinates
)
153 return NotImplemented
155 def angle(self
, other
):
157 Retrieve the angle required to rotate the vector into the vector passed
158 in argument. The result is an angle in radians, ranging between -pi and
161 if not isinstance(other
, Vector
):
162 raise TypeError('argument must be a Vector instance')
163 cosinus
= self
.dot(other
) / (self
.norm() * other
.norm())
164 return math
.acos(cosinus
)
166 def cross(self
, other
):
168 Calculate the cross product of two Vector3D structures.
170 if not isinstance(other
, Vector
):
171 raise TypeError('other must be a Vector instance')
172 if self
.dimension
!= 3 or other
.dimension
!= 3:
173 raise ValueError('arguments must be three-dimensional vectors')
174 if self
.symbols
!= other
.symbols
:
175 raise ValueError('arguments must belong to the same space')
176 x
, y
, z
= self
.symbols
178 coordinates
.append((x
, self
[y
]*other
[z
] - self
[z
]*other
[y
]))
179 coordinates
.append((y
, self
[z
]*other
[x
] - self
[x
]*other
[z
]))
180 coordinates
.append((z
, self
[x
]*other
[y
] - self
[y
]*other
[x
]))
181 return Vector(coordinates
)
183 def __truediv__(self
, other
):
185 Divide the vector by the specified scalar and returns the result as a
188 if not isinstance(other
, numbers
.Real
):
189 return NotImplemented
190 coordinates
= _map(self
, lambda coordinate
: coordinate
/ other
)
191 return Vector(coordinates
)
193 def dot(self
, other
):
195 Calculate the dot product of two vectors.
197 if not isinstance(other
, Vector
):
198 raise TypeError('argument must be a Vector instance')
200 for symbol
, coordinate1
, coordinate2
in _iter2(self
, other
):
201 result
+= coordinate1
* coordinate2
204 def __eq__(self
, other
):
205 return isinstance(other
, Vector
) and \
206 self
._coordinates
== other
._coordinates
209 return hash(tuple(self
.coordinates()))
211 def __mul__(self
, other
):
212 if not isinstance(other
, numbers
.Real
):
213 return NotImplemented
214 coordinates
= _map(self
, lambda coordinate
: other
* coordinate
)
215 return Vector(coordinates
)
220 coordinates
= _map(self
, operator
.neg
)
221 return Vector(coordinates
)
224 return math
.sqrt(self
.norm2())
228 for coordinate
in self
._coordinates
.values():
229 result
+= coordinate
** 2
233 return self
/ self
.norm()
235 def __sub__(self
, other
):
236 if isinstance(other
, (Point
, Vector
)):
237 coordinates
= _map2(self
, other
, operator
.sub
)
238 return other
.__class
__(coordinates
)
239 return NotImplemented
242 string
= ', '.join(['{!r}: {!r}'.format(symbol
, coordinate
)
243 for symbol
, coordinate
in self
.coordinates()])
244 return '{}({{{}}})'.format(self
.__class
__.__name
__, string
)