Contact
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
| Download

📚 The CoCalc Library - books, templates and other resources

Views: 96166
License: OTHER
1
"""Exploration of Vectors and Frames.
2
3
Copyright 2012 Allen B. Downey
4
License: GNU GPLv3 http://www.gnu.org/licenses/gpl.html
5
6
"""
7
8
from __future__ import print_function, division
9
10
import sys
11
import numpy
12
import math
13
14
def println(s):
15
print(s, '\n')
16
17
class FrameError(ValueError):
18
"""Represents a problem with frame of reference."""
19
20
class Vector:
21
def __init__(self, array, frame=None):
22
"""A vector is an array of coordinates and a frame of reference.
23
24
array:
25
frame: Frame object
26
"""
27
self.array = array
28
self.frame = frame
29
30
def __str__(self):
31
if self.frame == None:
32
return '^{O}%s' % (str(self.array), )
33
else:
34
return '^{%s}%s' % (str(self.frame), str(self.array))
35
36
def __add__(self, other):
37
if self.frame != other.frame:
38
raise FrameError("Vectors must be relative to the same frame.")
39
40
return Vector(self.array + other.array, self.frame)
41
42
@staticmethod
43
def from_list(t, frame=None):
44
"""Makes a vector from a list.
45
46
t: list of coordinates
47
frame: reference Frame
48
"""
49
return Vector(numpy.array(t), frame)
50
51
52
class Rotation:
53
def __init__(self, array):
54
self.array = array
55
56
def __str__(self):
57
return 'Rotation\n%s' % str(self.array)
58
59
def __neg__(self):
60
return Rotation(-self.array)
61
62
def __mul__(self, other):
63
"""Apply the rotation to a Vector."""
64
return numpy.dot(self.array, other.array)
65
66
__call__ = __mul__
67
68
@staticmethod
69
def from_axis(axis, theta):
70
x, y, z = numpy.ravel(axis.array)
71
c = math.cos(theta)
72
u = 1.0-c
73
s = math.sqrt(1.0-c*c)
74
xu, yu, zu = x*u, y*u, z*u
75
v1 = [x*xu + c, x*yu - z*s, x*zu + y*s]
76
v2 = [x*yu + z*s, y*yu + c, y*zu - x*s]
77
v3 = [x*zu - y*s, y*zu + x*s, z*zu + c]
78
return Rotation(numpy.array([v1, v2, v3]))
79
80
def to_axis(self):
81
# return the equivalent angle-axis as (khat, theta)
82
pass
83
84
def transpose(self):
85
return Rotation(numpy.transpose(self.array))
86
87
inverse = transpose
88
89
90
class Transform:
91
"""Represents a transform from one Frame to another."""
92
93
def __init__(self, rot, org, source=None):
94
"""Instantiates a Transform.
95
96
rot: Rotation object
97
org: origin Vector
98
source: source Frame
99
"""
100
self.rot = rot
101
self.org = org
102
self.dest = org.frame
103
self.source = source
104
self.source.add_transform(self)
105
106
def __str__(self):
107
"""Returns a string representation of the Transform."""
108
if self.dest == None:
109
return '%s' % self.source.name
110
return '_{%s}^{O}T' % self.source.name
111
else:
112
return '_{%s}^{%s}T' % (self.source.name, self.dest.name)
113
114
def __mul__(self, other):
115
"""Applies a Transform to a Vector or Transform."""
116
if isinstance(other, Vector):
117
return self.mul_vector(other)
118
119
if isinstance(other, Transform):
120
return self.mul_transform(other)
121
122
__call__ = __mul__
123
124
def mul_vector(self, p):
125
"""Applies a Transform to a Vector.
126
127
p: Vector
128
129
Returns: Vector
130
"""
131
if p.frame != self.source:
132
raise FrameError(
133
"The frame of the vector must be the source of the transform")
134
return Vector(self.rot * p, self.dest) + self.org
135
136
def mul_transform(self, other):
137
"""Applies a Transform to another Transform.
138
139
other: Transform
140
141
Returns Transform
142
"""
143
if other.dest != self.source:
144
raise FrameError(
145
"This frames source must be the other frame's destination.")
146
147
rot = Rotation(self.rot * other.rot)
148
t = Transform(rot, self * other.org, other.source)
149
return t
150
151
def inverse(self):
152
"""Computes the inverse transform.
153
154
Returns: Transform
155
"""
156
irot = self.rot.inverse()
157
iorg = Vector(-(irot * self.org), self.source)
158
t = Transform(irot, iorg, self.dest)
159
return t
160
161
162
class Frame:
163
"""Represents a frame of reference."""
164
165
# list of Frames
166
roster = []
167
168
def __init__(self, name):
169
"""Instantiate a Frame.
170
171
name: string
172
"""
173
self.name = name
174
self.transforms = {}
175
Frame.roster.append(self)
176
177
def __str__(self):
178
return self.name
179
180
def add_transform(self, transform):
181
"""A frames is defined by a Transform relative to another Frame.
182
183
transform: Transform object
184
"""
185
if transform.source != self:
186
raise FrameError("Source of the transform must be this Frame.")
187
188
if transform.dest:
189
self.transforms[transform.dest] = transform
190
191
def dests(self):
192
"""Returns a list of the Frames we know how to Transform to."""
193
return self.transforms.keys()
194
195
196
class Vertex:
197
"""Represents a node in a graph."""
198
199
def __init__(self, frame):
200
self.frame = frame
201
self.dist = 1000000
202
self.out = []
203
204
def __str__(self):
205
return '%s %d' % (self.frame.name, self.dist)
206
207
208
def shortest_path(start, frames):
209
"""For a given list of frames and a starting frame,
210
find the shortest path of transforms from the
211
starting frame to all other frames.
212
The 'distance' is the number of inverse transformations
213
that must be calculated.
214
The result is a dictionary of vertices, where
215
each vertex is labeled with the frame it corresponds
216
to, the distance from the starting frame, and the prev
217
frame along the path from start. """
218
219
map = dict([(f, Vertex(f)) for f in frames])
220
221
length = {}
222
for v in map.values():
223
for dest in v.frame.transforms:
224
w = map[dest]
225
v.out.append(w)
226
length[(v, w)] = 0
227
228
w.out.append(v)
229
length[(w, v)] = 1
230
231
s = map[start]
232
s.dist = 0
233
queue = [s]
234
235
while queue:
236
v = queue.pop()
237
for w in v.out:
238
d = v.dist + length[(v,w)]
239
if d < w.dist:
240
w.dist = d
241
w.prev = v
242
if w not in queue: queue.append(w)
243
244
return map
245
246
def print_shortest_path(map):
247
for source, v in map.items():
248
try:
249
v.prev
250
print(source, v.dist, v.prev.frame)
251
except:
252
print(source, v.dist)
253
254
def print_length(length):
255
for v, w in length:
256
print(v.frame.name, w.frame.name, length[(v, w)])
257
print()
258
259
260
def main(name):
261
262
theta = math.pi/2
263
264
#v_o = Vector.from_list([0, 0, 0], None)
265
origin = Frame('O')
266
#o_trans = Transform(None, v_o, origin)
267
268
xhat = Vector.from_list([1, 0, 0], origin)
269
rx = Rotation.from_axis(xhat, theta)
270
a = Frame('A')
271
t_ao = Transform(rx, xhat, a)
272
273
yhat = Vector.from_list([0, 1, 0], a)
274
ry = Rotation.from_axis(yhat, theta)
275
b = Frame('B')
276
t_ba = Transform(ry, yhat, b)
277
278
zhat = Vector.from_list([0, 0, 1], b)
279
rz = Rotation.from_axis(zhat, theta)
280
c = Frame('C')
281
t_cb = Transform(rz, zhat, c)
282
283
p_c = Vector.from_list([1, 1, 1], c)
284
println(p_c)
285
286
p_b = t_cb(p_c)
287
println(p_b)
288
289
p_a = t_ba(p_b)
290
println(p_a)
291
292
p = t_ao(p_a)
293
println(p)
294
295
map = shortest_path(origin, Frame.roster)
296
print_shortest_path(map)
297
298
cbao = t_ao(t_ba(t_cb))
299
p = cbao(p_c)
300
println(p)
301
302
inv = cbao.inverse()
303
p_c = inv(p)
304
println(p_c)
305
306
map = shortest_path(origin, Frame.roster)
307
print_shortest_path(map)
308
309
310
if __name__ == '__main__':
311
main(*sys.argv)
312
313