from numpy import linspace, sin, cos, pi, array, asarray, ndarray, sqrt, abs import pprint from MatplotlibDraw import MatplotlibDraw drawing_tool = MatplotlibDraw() def point(x, y): return array((x, y), dtype=float) class Shape: """ Superclass for drawing different geometric shapes. Subclasses define shapes, but drawing, rotation, translation, etc. are done in generic functions in this superclass. """ def __init__(self): """ Until new version of shapes.py is ready: Never to be called from subclasses. """ raise NotImplementedError( 'class %s must implement __init__,\nwhich defines ' 'self.shapes as a list of Shape objects\n' '(and preferably self._repr string).\n' 'Do not call Shape.__init__!' % \ self.__class__.__name__) def __iter__(self): # We iterate over self.shapes many places, and will # get here if self.shapes is just a Shape object and # not the assumed list. print 'Warning: class %s does not define self.shapes\n'\ 'as a *list* of Shape objects' return [self] # Make the iteration work def for_all_shapes(self, func, *args, **kwargs): if not hasattr(self, 'shapes'): # When self.shapes is lacking, we either come to # a special implementation of func or we come here # because Shape.func is just inherited. This is # an error if the class is not Curve or Point if isinstance(self, (Curve, Point)): return # ok: no shapes and no func else: raise AttributeError('class %s has no shapes attribute!' % self.__class__.__name__) is_dict = True if isinstance(self.shapes, dict) else False for shape in self.shapes: if is_dict: shape = self.shapes[shape] getattr(shape, func)(*args, **kwargs) def draw(self): self.for_all_shapes('draw') def rotate(self, angle, center=point(0,0)): self.for_all_shapes('rotate', angle, center) def translate(self, vec): self.for_all_shapes('translate', vec) def scale(self, factor): self.for_all_shapes('scale', factor) def set_linestyle(self, style): self.for_all_shapes('set_linestyle', style) def set_linewidth(self, width): self.for_all_shapes('set_linewidth', width) def set_linecolor(self, color): self.for_all_shapes('set_linecolor', color) def set_arrow(self, style): self.for_all_shapes('set_arrow', style) def set_name(self, name): self.for_all_shapes('set_name', name) def set_filled_curves(self, fillcolor='', fillhatch=''): self.for_all_shapes('set_filled_curves', fillcolor, fillhatch) def __str__(self): return self.__class__.__name__ def __repr__(self): #print 'repr in class', self.__class__.__name__ return pprint.pformat(self.shapes) class Curve(Shape): """General curve as a sequence of (x,y) coordintes.""" def __init__(self, x, y): """ `x`, `y`: arrays holding the coordinates of the curve. """ self.x, self.y = x, y # Turn to numpy arrays self.x = asarray(self.x, dtype=float) self.y = asarray(self.y, dtype=float) #self.shapes must not be defined in this class #as self.shapes holds children objects: #Curve has no children (end leaf of self.shapes tree) self.linestyle = None self.linewidth = None self.linecolor = None self.fillcolor = None self.fillhatch = None self.arrow = None self.name = None def inside_plot_area(self, verbose=True): """Check that all coordinates are within drawing_tool's area.""" xmin, xmax = self.x.min(), self.x.max() ymin, ymax = self.y.min(), self.y.max() t = drawing_tool inside = True if xmin < t.xmin: inside = False if verbose: print 'x_min=%g < plot area x_min=%g' % (xmin, t.xmin) if xmax > t.xmax: inside = False if verbose: print 'x_max=%g > plot area x_max=%g' % (xmax, t.xmax) if ymin < t.ymin: inside = False if verbose: print 'y_min=%g < plot area y_min=%g' % (ymin, t.ymin) if xmax > t.xmax: inside = False if verbose: print 'y_max=%g > plot area y_max=%g' % (ymax, t.ymax) return inside def draw(self): self.inside_plot_area() drawing_tool.define_curve( self.x, self.y, self.linestyle, self.linewidth, self.linecolor, self.arrow, self.fillcolor, self.fillhatch) def rotate(self, angle, center=point(0,0)): """ Rotate all coordinates: `angle` is measured in degrees and (`x`,`y`) is the "origin" of the rotation. """ angle = angle*pi/180 x, y = center c = cos(angle); s = sin(angle) xnew = x + (self.x - x)*c - (self.y - y)*s ynew = y + (self.x - x)*s + (self.y - y)*c self.x = xnew self.y = ynew def scale(self, factor): """Scale all coordinates by `factor`: ``x = factor*x``, etc.""" self.x = factor*self.x self.y = factor*self.y def translate(self, vec): """Translate all coordinates by a vector `vec`.""" self.x += vec[0] self.y += vec[1] def set_linecolor(self, color): self.linecolor = color def set_linewidth(self, width): self.linewidth = width def set_linestyle(self, style): self.linestyle = style def set_arrow(self, style=None): styles = ('->', '<-', '<->') if not style in styles: raise ValueError('style=%s must be in %s' % (style, styles)) self.arrow = style def set_name(self, name): self.name = name def set_filled_curves(self, fillcolor='', fillhatch=''): self.fillcolor = fillcolor self.fillhatch = fillhatch def __str__(self): s = '%d (x,y) coordinates' % self.x.size if not self.inside_plot_area(verbose=False): s += ', some coordinates are outside plotting area!\n' props = ('linecolor', 'linewidth', 'linestyle', 'arrow', 'name', 'fillcolor', 'fillhatch') for prop in props: value = getattr(self, prop) if value is not None: s += ' %s: "%s"' % (prop, value) return s def __repr__(self): return str(self) class Point(Shape): """A point (x,y) which can be rotated, translated, and scaled.""" def __init__(self, x, y): self.x, self.y = x, y #self.shapes is not needed in this class def __add__(self, other): if isinstance(other, (list,tuple)): other = Point(other) return Point(self.x+other.x, self.y+other.y) # class Point is an abstract class - only subclasses are useful # and must implement draw def draw(self): raise NotImplementedError( 'class %s must implement the draw method' % self.__class__.__name__) def rotate(self, angle, center=point(0,0)): """Rotate point an `angle` (in degrees) around (`x`,`y`).""" angle = angle*pi/180 x, y = center c = cos(angle); s = sin(angle) xnew = x + (self.x - x)*c - (self.y - y)*s ynew = y + (self.x - x)*s + (self.y - y)*c self.x = xnew self.y = ynew def scale(self, factor): """Scale point coordinates by `factor`: ``x = factor*x``, etc.""" self.x = factor*self.x self.y = factor*self.y def translate(self, vec): """Translate point by a vector `vec`.""" self.x += vec[0] self.y += vec[1] # no need to store input data as they are invalid after rotations etc. class Rectangle(Shape): def __init__(self, lower_left_corner, width, height): ll = lower_left_corner # short form x = [ll[0], ll[0] + width, ll[0] + width, ll[0], ll[0]] y = [ll[1], ll[1], ll[1] + height, ll[1] + height, ll[1]] self.shapes = {'rectangle': Curve(x,y)} class Triangle(Shape): """Triangle defined by its three vertices p1, p2, and p3.""" def __init__(self, p1, p2, p3): x = [p1[0], p2[0], p3[0], p1[0]] y = [p1[1], p2[1], p3[1], p1[1]] self.shapes = {'triangle': Curve(x,y)} class Line(Shape): def __init__(self, start, stop): x = [start[0], stop[0]] y = [start[1], stop[1]] self.shapes = {'line': Curve(x, y)} self.compute_formulas() def compute_formulas(self): x, y = self.shapes['line'].x, self.shapes['line'].y # Define equations for line: # y = a*x + b, x = c*y + d try: self.a = (y[1] - y[0])/(x[1] - x[0]) self.b = y[0] - self.a*x[0] except ZeroDivisionError: # Vertical line, y is not a function of x self.a = None self.b = None try: if self.a is None: self.c = 0 else: self.c = 1/float(self.a) if self.b is None: self.d = x[1] except ZeroDivisionError: # Horizontal line, x is not a function of y self.c = None self.d = None def __call__(self, x=None, y=None): """Given x, return y on the line, or given y, return x.""" self.compute_formulas() if x is not None and self.a is not None: return self.a*x + self.b elif y is not None and self.c is not None: return self.c*y + self.d else: raise ValueError( 'Line.__call__(x=%s, y=%s) not meaningful' % \ (x, y)) # First implementation of class Circle class Circle(Shape): def __init__(self, center, radius, resolution=180): self.center, self.radius = center, radius self.resolution = resolution t = linspace(0, 2*pi, resolution+1) x0 = center[0]; y0 = center[1] R = radius x = x0 + R*cos(t) y = y0 + R*sin(t) self.shapes = {'circle': Curve(x, y)} def __call__(self, theta): """Return (x, y) point corresponding to theta.""" return self.center[0] + self.radius*cos(theta), \ self.center[1] + self.radius*sin(theta) class Arc(Shape): def __init__(self, center, radius, start_degrees, opening_degrees, resolution=180): self.center = center self.radius = radius self.start_degrees = start_degrees*pi/180 # radians self.opening_degrees = opening_degrees*pi/180 self.resolution = resolution t = linspace(self.start_degrees, self.start_degrees + self.opening_degrees, resolution+1) x0 = center[0]; y0 = center[1] R = radius x = x0 + R*cos(t) y = y0 + R*sin(t) self.shapes = {'arc': Curve(x, y)} def __call__(self, theta): """Return (x,y) point at start_degrees + theta.""" theta = theta*pi/180 t = self.start_degrees + theta x0 = self.center[0] y0 = self.center[1] R = self.radius x = x0 + R*cos(t) y = y0 + R*sin(t) return (x, y) # Alternative for small arcs: Parabola class Parabola(Shape): def __init__(self, start, mid, stop, resolution=21): self.p1, self.p2, self.p3 = start, mid, stop # y as function of x? (no point on line x=const?) tol = 1E-14 if abs(self.p1[0] - self.p2[0]) > 1E-14 and \ abs(self.p2[0] - self.p3[0]) > 1E-14 and \ abs(self.p3[0] - self.p1[0]) > 1E-14: self.y_of_x = True else: self.y_of_x = False # x as function of y? (no point on line y=const?) tol = 1E-14 if abs(self.p1[1] - self.p2[1]) > 1E-14 and \ abs(self.p2[1] - self.p3[1]) > 1E-14 and \ abs(self.p3[1] - self.p1[1]) > 1E-14: self.x_of_y = True else: self.x_of_y = False if self.y_of_x: x = linspace(start[0], end[0], resolution) y = self(x=x) elif self.x_of_y: y = linspace(start[1], end[1], resolution) x = self(y=y) else: raise ValueError( 'Parabola: two or more points lie on x=const ' 'or y=const - not allowed') self.shapes = {'parabola': Curve(x, y)} def __call__(self, x=None, y=None): if x is not None and self.y_of_x: return self._L2x(self.p1, self.p2)*self.p3[1] + \ self._L2x(self.p2, self.p3)*self.p1[1] + \ self._L2x(self.p3, self.p1)*self.p2[1] elif y is not None and self.x_of_y: return self._L2y(self.p1, self.p2)*self.p3[0] + \ self._L2y(self.p2, self.p3)*self.p1[0] + \ self._L2y(self.p3, self.p1)*self.p2[0] else: raise ValueError( 'Parabola.__call__(x=%s, y=%s) not meaningful' % \ (x, y)) def _L2x(self, x, pi, pj, pk): return (x - pi[0])*(x - pj[0])/((pk[0] - pi[0])*(pk[0] - pj[0])) def _L2y(self, y, pi, pj, pk): return (y - pi[1])*(y - pj[1])/((pk[1] - pi[1])*(pk[1] - pj[1])) class Circle(Arc): def __init__(self, center, radius, resolution=180): Arc.__init__(self, center, radius, 0, 360, resolution) # class Wall: horizontal Line with many small Lines 45 degrees class XWall(Shape): def __init__(start, length, dx, below=True): n = int(round(length/float(dx))) # no of intervals x = linspace(start[0], start[0] + length, n+1) y = start[1] dy = dx if below: taps = [Line((xi,y-dy), (xi+dx, y)) for xi in x[:-1]] else: taps = [Line((xi,y), (xi+dx, y+dy)) for xi in x[:-1]] self.shapes = [Line(start, (start[0]+length, start[1]))] + taps class Wall(Shape): def __init__(self, start, length, thickness, rotation_angle=0): p1 = asarray(start) p2 = p1 + asarray([length, 0]) p3 = p2 + asarray([0, thickness]) p4 = p1 + asarray([0, thickness]) p5 = p1 x = [p[0] for p in p1, p2, p3, p4, p5] y = [p[1] for p in p1, p2, p3, p4, p5] wall = Curve(x, y) wall.set_filled_curves('white', '/') wall.rotate(rotation_angle, start) self.shapes = {'wall': wall} """ def draw(self): x = self.shapes['wall'].x y = self.shapes['wall'].y drawing_tool.ax.fill(x, y, 'w', edgecolor=drawing_tool.linecolor, hatch='/') """ class CurveWall(Shape): def __init__(self, x, y, thickness): x1 = asarray(x, float) y1 = asarray(y, float) x2 = x1 y2 = y1 + thickness from numpy import concatenate # x1/y1 + reversed x2/y2 x = concatenate((x1, x2[-1::-1])) y = concatenate((y1, y2[-1::-1])) wall = Curve(x, y) wall.set_filled_curves('white', '/') self.shapes = {'wall': wall} """ def draw(self): x = self.shapes['wall'].x y = self.shapes['wall'].y drawing_tool.ax.fill(x, y, 'w', edgecolor=drawing_tool.linecolor, hatch='/') """ class Text(Point): def __init__(self, text, position, alignment='center', fontsize=18): self.text = text self.alignment, self.fontsize = alignment, fontsize is_sequence(position, length=2, can_be_None=True) Point.__init__(self, position[0], position[1]) #no need for self.shapes here def draw(self): drawing_tool.text(self.text, (self.x, self.y), self.alignment, self.fontsize) def __str__(self): return 'text "%s" at (%g,%g)' % (self.text, self.x, self.y) def __repr__(self): return str(self) class Text_wArrow(Text): def __init__(self, text, position, arrow_tip, alignment='center', fontsize=18): is_sequence(arrow_tip, length=2, can_be_None=True) self.arrow_tip = arrow_tip Text.__init__(self, text, position, alignment, fontsize) def draw(self): drawing_tool.text(self.text, self.position, self.alignment, self.fontsize, self.arrow_tip) def __str__(self): return 'annotation "%s" at (%g,%g) with arrow to (%g,%g)' % \ (self.text, self.x, self.y, self.arrow_tip[0], self.arrow_tip[1]) def __repr__(self): return str(self) class Axis(Shape): def __init__(self, bottom_point, length, label, below=True, rotation_angle=0, label_spacing=1./25): """ Draw axis from bottom_point with `length` to the right (x axis). Place label below (True) or above (False) axis. Then return `rotation_angle` (in degrees). To make a standard x axis, call with ``below=True`` and ``rotation_angle=0``. To make a standard y axis, call with ``below=False`` and ``rotation_angle=90``. A tilted axis can also be drawn. The `label_spacing` denotes the space between the symbol and the arrow tip as a fraction of the length of the plot in x direction. """ # Arrow is vertical arrow, make it horizontal arrow = Arrow(bottom_point, length, rotation_angle=-90) arrow.rotate(rotation_angle, bottom_point) spacing = drawing_tool.xrange*label_spacing if below: spacing = - spacing label_pos = [bottom_point[0] + length, bottom_point[1] + spacing] symbol = Text(label, position=label_pos) symbol.rotate(rotation_angle, bottom_point) self.shapes = {'arrow': arrow, 'symbol': symbol} class Gravity(Axis): """Downward-pointing gravity arrow with the symbol g.""" def __init__(self, start, length): Axis.__init__(self, start, length, '$g$', below=False, rotation_angle=-90, label_spacing=1./30) def test_Axis(): set_coordinate_system(xmin=0, xmax=15, ymin=0, ymax=15, axis=True) x_axis = Axis((7.5,2), 5, 'x', rotation_angle=0) y_axis = Axis((7.5,2), 5, 'y', below=False, rotation_angle=90) system = Compose({'x axis': x_axis, 'y axis': y_axis}) system.draw() drawing_tool.display() set_linestyle('dashed') system.shapes['x axis'].rotate(40, (7.5, 2)) system.shapes['y axis'].rotate(40, (7.5, 2)) system.draw() drawing_tool.display() print repr(system) class DistanceSymbol(Shape): """ Arrow with symbol at the midpoint, for identifying a distance with a symbol. """ def __init__(self, start, end, symbol, fontsize=14): start = asarray(start, float) end = asarray(end, float) mid = 0.5*(start + end) # midpoint of start-end line tangent = end - start normal = asarray([-tangent[1], tangent[0]])/\ sqrt(tangent[0]**2 + tangent[1]**2) symbol_pos = mid + normal*drawing_tool.xrange/60. self.shapes = {'arrow': Arrow1(start, end, style='<->'), 'symbol': Text(symbol, symbol_pos, fontsize=fontsize)} class ArcSymbol(Shape): def __init__(self, symbol, center, radius, start_degrees, opening_degrees, resolution=180, fontsize=14): arc = Arc(center, radius, start_degrees, opening_degrees, resolution) mid = asarray(arc(opening_degrees/2.)) normal = mid - asarray(center, float) normal = normal/sqrt(normal[0]**2 + normal[1]**2) symbol_pos = mid + normal*drawing_tool.xrange/60. self.shapes = {'arc': arc, 'symbol': Text(symbol, symbol_pos, fontsize=fontsize)} class Compose(Shape): def __init__(self, shapes): """shapes: list or dict of Shape objects.""" self.shapes = shapes # can make help methods: Line.midpoint, Line.normal(pt, dir='left') -> (x,y) # list annotations in each class? contains extra annotations for explaining # important parameters to the constructor, e.g., Line.annotations holds # start and end as Text objects. Shape.demo calls shape.draw and # for annotation in self.demo: annotation.draw() YES! # Can make overall demo of classes by making objects and calling demo # Could include demo fig in each constructor class Arrow1(Shape): """Draw an arrow as Line with arrow.""" def __init__(self, start, end, style='->'): self.start, self.end, self.style = start, end, style self.shapes = {'arrow': Line(start, end, arrow=style)} class Arrow3(Shape): """Draw a vertical line and arrow head. Then rotate `rotation_angle`.""" def __init__(self, bottom_point, length, rotation_angle=0): self.bottom = bottom_point self.length = length self.angle = rotation_angle top = (self.bottom[0], self.bottom[1] + self.length) main = Line(self.bottom, top) #head_length = self.length/8.0 head_length = drawing_tool.xrange/50. head_degrees = 30*pi/180 head_left_pt = (top[0] - head_length*sin(head_degrees), top[1] - head_length*cos(head_degrees)) head_right_pt = (top[0] + head_length*sin(head_degrees), top[1] - head_length*cos(head_degrees)) head_left = Line(head_left_pt, top) head_right = Line(head_right_pt, top) head_left.set_linestyle('solid') head_right.set_linestyle('solid') self.shapes = {'line': main, 'head left': head_left, 'head right': head_right} # rotate goes through self.shapes so this must be initialized first self.rotate(rotation_angle, bottom_point) Arrow = Arrow3 # backward compatibility class Wheel(Shape): def __init__(self, center, radius, inner_radius=None, nlines=10): self.center = center self.radius = radius if inner_radius is None: self.inner_radius = radius/5.0 else: self.inner_radius = inner_radius self.nlines = nlines outer = Circle(self.center, self.radius) inner = Circle(self.center, self.inner_radius) lines = [] # Draw nlines+1 since the first and last coincide # (then nlines lines will be visible) t = linspace(0, 2*pi, self.nlines+1) Ri = self.inner_radius; Ro = self.radius x0 = self.center[0]; y0 = self.center[1] xinner = x0 + Ri*cos(t) yinner = y0 + Ri*sin(t) xouter = x0 + Ro*cos(t) youter = y0 + Ro*sin(t) lines = [Line((xi,yi),(xo,yo)) for xi, yi, xo, yo in \ zip(xinner, yinner, xouter, youter)] self.shapes = [outer, inner] + lines class Wave(Shape): def __init__(self, xstart, xstop, wavelength, amplitude, mean_level): self.xstart = xstart self.xstop = xstop self.wavelength = wavelength self.amplitude = amplitude self.mean_level = mean_level npoints = (self.xstop - self.xstart)/(self.wavelength/61.0) x = linspace(self.xstart, self.xstop, npoints) k = 2*pi/self.wavelength # frequency y = self.mean_level + self.amplitude*sin(k*x) self.shapes = {'waves': Curve(x,y)} # make a version of Spring using Point class class Spring(Shape): def __init__(self, bottom_point, length, tagwidth, ntags=4): """ Specify a vertical spring, starting at bottom_point and having a specified lengths. In the middle third of the spring there are ntags tags. """ self.B = bottom_point self.n = ntags - 1 # n counts tag intervals # n must be odd: if self.n % 2 == 0: self.n = self.n+1 self.L = length self.w = tagwidth B, L, n, w = self.B, self.L, self.n, self.w # short forms t = L/(3.0*n) # must be better worked out P0 = (B[0], B[1]+L/3.0) P1 = (B[0], B[1]+L/3.0+t/2.0) P2 = (B[0], B[1]+L*2/3.0) P3 = (B[0], B[1]+L) line1 = Line(B, P1) lines = [line1] #line2 = Line(P2, P3) T1 = P1 T2 = (T1[0] + w, T1[1] + t/2.0) lines.append(Line(T1,T2)) T1 = (T2[0], T2[1]) for i in range(n): T2 = (T1[0] + (-1)**(i+1)*2*w, T1[1] + t/2.0) lines.append(Line(T1, T2)) T1 = (T2[0], T2[1]) T2 = (T1[0] + w, T1[1] + t/2.0) lines.append(Line(T1,T2)) #print P2, T2 lines.append(Line(T2, P3)) self.shapes = lines # COMPOSITE types: # MassSpringForce: Line(horizontal), Spring, Rectangle, Arrow/Line(w/arrow) # must be easy to find the tip of the arrow # Maybe extra dict: self.name['mass'] = Rectangle object - YES! def _test1(): set_coordinate_system(xmin=0, xmax=10, ymin=0, ymax=10) l1 = Line((0,0), (1,1)) l1.draw() input(': ') c1 = Circle((5,2), 1) c2 = Circle((6,2), 1) w1 = Wheel((7,2), 1) c1.draw() c2.draw() w1.draw() hardcopy() display() # show the plot def _test2(): set_coordinate_system(xmin=0, xmax=10, ymin=0, ymax=10) l1 = Line((0,0), (1,1)) l1.draw() input(': ') c1 = Circle((5,2), 1) c2 = Circle((6,2), 1) w1 = Wheel((7,2), 1) filled_curves(True) set_linecolor('blue') c1.draw() set_linecolor('aqua') c2.draw() filled_curves(False) set_linecolor('red') w1.draw() hardcopy() display() # show the plot def _test3(): """Test example from the book.""" set_coordinate_system(xmin=0, xmax=10, ymin=0, ymax=10) l1 = Line(start=(0,0), stop=(1,1)) # define line l1.draw() # make plot data r1 = Rectangle(lower_left_corner=(0,1), width=3, height=5) r1.draw() Circle(center=(5,7), radius=1).draw() Wheel(center=(6,2), radius=2, inner_radius=0.5, nlines=7).draw() hardcopy() display() def _test4(): """Second example from the book.""" set_coordinate_system(xmin=0, xmax=10, ymin=0, ymax=10) r1 = Rectangle(lower_left_corner=(0,1), width=3, height=5) c1 = Circle(center=(5,7), radius=1) w1 = Wheel(center=(6,2), radius=2, inner_radius=0.5, nlines=7) c2 = Circle(center=(7,7), radius=1) filled_curves(True) c1.draw() set_linecolor('blue') r1.draw() set_linecolor('aqua') c2.draw() # Add thick aqua line around rectangle: filled_curves(False) set_linewidth(4) r1.draw() set_linecolor('red') # Draw wheel with thick lines: w1.draw() hardcopy('tmp_colors') display() def _test5(): set_coordinate_system(xmin=0, xmax=10, ymin=0, ymax=10) c = 6. # center point of box w = 2. # size of box L = 3 r1 = Rectangle((c-w/2, c-w/2), w, w) l1 = Line((c,c-w/2), (c,c-w/2-L)) linecolor('blue') filled_curves(True) r1.draw() linecolor('aqua') filled_curves(False) l1.draw() hardcopy() display() # show the plot def rolling_wheel(total_rotation_angle): """Animation of a rotating wheel.""" set_coordinate_system(xmin=0, xmax=10, ymin=0, ymax=10) import time center = (6,2) radius = 2.0 angle = 2.0 pngfiles = [] w1 = Wheel(center=center, radius=radius, inner_radius=0.5, nlines=7) for i in range(int(total_rotation_angle/angle)): w1.draw() print 'XXXXXXXXXXXXXXXXXXXXXX BIG PROBLEM WITH ANIMATE!!!' display() filename = 'tmp_%03d' % i pngfiles.append(filename + '.png') hardcopy(filename) time.sleep(0.3) # pause L = radius*angle*pi/180 # translation = arc length w1.rotate(angle, center) w1.translate((-L, 0)) center = (center[0] - L, center[1]) erase() cmd = 'convert -delay 50 -loop 1000 %s tmp_movie.gif' \ % (' '.join(pngfiles)) print 'converting PNG files to animated GIF:\n', cmd import commands failure, output = commands.getstatusoutput(cmd) if failure: print 'Could not run', cmd def is_sequence(seq, length=None, can_be_None=False, error_message=True): if can_be_None: legal_types = (list,tuple,ndarray,None) else: legal_types = (list,tuple,ndarray) if isinstance(seq, legal_types): if length is not None: if length == len(seq): return True elif error_message: raise TypeError('%s is %s; must be %s of length %d' % (str(point), type(point), ', '.join([str(t) for t in legal_types]), len(seq))) else: return False else: return True elif error_message: raise TypeError('%s is %s; must be %s' % str(point), type(point), ', '.join([str(t) for t in legal_types])) else: return False if __name__ == '__main__': #rolling_wheel(40) #_test1() #_test3() funcs = [ #test_Axis, test_inclined_plane, ] for func in funcs: func() raw_input('Type Return: ')