📚 The CoCalc Library - books, templates and other resources
License: OTHER
"""Exploration of Vectors and Frames.12Copyright 2012 Allen B. Downey3License: GNU GPLv3 http://www.gnu.org/licenses/gpl.html45"""67from __future__ import print_function, division89import sys10import numpy11import math1213def println(s):14print(s, '\n')1516class FrameError(ValueError):17"""Represents a problem with frame of reference."""1819class Vector:20def __init__(self, array, frame=None):21"""A vector is an array of coordinates and a frame of reference.2223array:24frame: Frame object25"""26self.array = array27self.frame = frame2829def __str__(self):30if self.frame == None:31return '^{O}%s' % (str(self.array), )32else:33return '^{%s}%s' % (str(self.frame), str(self.array))3435def __add__(self, other):36if self.frame != other.frame:37raise FrameError("Vectors must be relative to the same frame.")3839return Vector(self.array + other.array, self.frame)4041@staticmethod42def from_list(t, frame=None):43"""Makes a vector from a list.4445t: list of coordinates46frame: reference Frame47"""48return Vector(numpy.array(t), frame)495051class Rotation:52def __init__(self, array):53self.array = array5455def __str__(self):56return 'Rotation\n%s' % str(self.array)5758def __neg__(self):59return Rotation(-self.array)6061def __mul__(self, other):62"""Apply the rotation to a Vector."""63return numpy.dot(self.array, other.array)6465__call__ = __mul__6667@staticmethod68def from_axis(axis, theta):69x, y, z = numpy.ravel(axis.array)70c = math.cos(theta)71u = 1.0-c72s = math.sqrt(1.0-c*c)73xu, yu, zu = x*u, y*u, z*u74v1 = [x*xu + c, x*yu - z*s, x*zu + y*s]75v2 = [x*yu + z*s, y*yu + c, y*zu - x*s]76v3 = [x*zu - y*s, y*zu + x*s, z*zu + c]77return Rotation(numpy.array([v1, v2, v3]))7879def to_axis(self):80# return the equivalent angle-axis as (khat, theta)81pass8283def transpose(self):84return Rotation(numpy.transpose(self.array))8586inverse = transpose878889class Transform:90"""Represents a transform from one Frame to another."""9192def __init__(self, rot, org, source=None):93"""Instantiates a Transform.9495rot: Rotation object96org: origin Vector97source: source Frame98"""99self.rot = rot100self.org = org101self.dest = org.frame102self.source = source103self.source.add_transform(self)104105def __str__(self):106"""Returns a string representation of the Transform."""107if self.dest == None:108return '%s' % self.source.name109return '_{%s}^{O}T' % self.source.name110else:111return '_{%s}^{%s}T' % (self.source.name, self.dest.name)112113def __mul__(self, other):114"""Applies a Transform to a Vector or Transform."""115if isinstance(other, Vector):116return self.mul_vector(other)117118if isinstance(other, Transform):119return self.mul_transform(other)120121__call__ = __mul__122123def mul_vector(self, p):124"""Applies a Transform to a Vector.125126p: Vector127128Returns: Vector129"""130if p.frame != self.source:131raise FrameError(132"The frame of the vector must be the source of the transform")133return Vector(self.rot * p, self.dest) + self.org134135def mul_transform(self, other):136"""Applies a Transform to another Transform.137138other: Transform139140Returns Transform141"""142if other.dest != self.source:143raise FrameError(144"This frames source must be the other frame's destination.")145146rot = Rotation(self.rot * other.rot)147t = Transform(rot, self * other.org, other.source)148return t149150def inverse(self):151"""Computes the inverse transform.152153Returns: Transform154"""155irot = self.rot.inverse()156iorg = Vector(-(irot * self.org), self.source)157t = Transform(irot, iorg, self.dest)158return t159160161class Frame:162"""Represents a frame of reference."""163164# list of Frames165roster = []166167def __init__(self, name):168"""Instantiate a Frame.169170name: string171"""172self.name = name173self.transforms = {}174Frame.roster.append(self)175176def __str__(self):177return self.name178179def add_transform(self, transform):180"""A frames is defined by a Transform relative to another Frame.181182transform: Transform object183"""184if transform.source != self:185raise FrameError("Source of the transform must be this Frame.")186187if transform.dest:188self.transforms[transform.dest] = transform189190def dests(self):191"""Returns a list of the Frames we know how to Transform to."""192return self.transforms.keys()193194195class Vertex:196"""Represents a node in a graph."""197198def __init__(self, frame):199self.frame = frame200self.dist = 1000000201self.out = []202203def __str__(self):204return '%s %d' % (self.frame.name, self.dist)205206207def shortest_path(start, frames):208"""For a given list of frames and a starting frame,209find the shortest path of transforms from the210starting frame to all other frames.211The 'distance' is the number of inverse transformations212that must be calculated.213The result is a dictionary of vertices, where214each vertex is labeled with the frame it corresponds215to, the distance from the starting frame, and the prev216frame along the path from start. """217218map = dict([(f, Vertex(f)) for f in frames])219220length = {}221for v in map.values():222for dest in v.frame.transforms:223w = map[dest]224v.out.append(w)225length[(v, w)] = 0226227w.out.append(v)228length[(w, v)] = 1229230s = map[start]231s.dist = 0232queue = [s]233234while queue:235v = queue.pop()236for w in v.out:237d = v.dist + length[(v,w)]238if d < w.dist:239w.dist = d240w.prev = v241if w not in queue: queue.append(w)242243return map244245def print_shortest_path(map):246for source, v in map.items():247try:248v.prev249print(source, v.dist, v.prev.frame)250except:251print(source, v.dist)252253def print_length(length):254for v, w in length:255print(v.frame.name, w.frame.name, length[(v, w)])256print()257258259def main(name):260261theta = math.pi/2262263#v_o = Vector.from_list([0, 0, 0], None)264origin = Frame('O')265#o_trans = Transform(None, v_o, origin)266267xhat = Vector.from_list([1, 0, 0], origin)268rx = Rotation.from_axis(xhat, theta)269a = Frame('A')270t_ao = Transform(rx, xhat, a)271272yhat = Vector.from_list([0, 1, 0], a)273ry = Rotation.from_axis(yhat, theta)274b = Frame('B')275t_ba = Transform(ry, yhat, b)276277zhat = Vector.from_list([0, 0, 1], b)278rz = Rotation.from_axis(zhat, theta)279c = Frame('C')280t_cb = Transform(rz, zhat, c)281282p_c = Vector.from_list([1, 1, 1], c)283println(p_c)284285p_b = t_cb(p_c)286println(p_b)287288p_a = t_ba(p_b)289println(p_a)290291p = t_ao(p_a)292println(p)293294map = shortest_path(origin, Frame.roster)295print_shortest_path(map)296297cbao = t_ao(t_ba(t_cb))298p = cbao(p_c)299println(p)300301inv = cbao.inverse()302p_c = inv(p)303println(p_c)304305map = shortest_path(origin, Frame.roster)306print_shortest_path(map)307308309if __name__ == '__main__':310main(*sys.argv)311312313