#***************************************************************************** # Copyright (C) 2007 Dean Moore < dino@boulder.net > # # Distributed under the terms of the GNU General Public License (GPL) # http://www.gnu.org/licenses/ #***************************************************************************** ################################################################################# # # # Hypotrochoid. Written by Dean Moore, February 2008 # # # # Inspiration: # # # # But a SAGE newbie & out to do projects & learn more, one day I was surfing # # Wikipedia and hit < http://en.wikipedia.org/wiki/Hypotrochoid >, saw # # the animated graph, and though, ... # # # # "I bet I can make SAGE do that." # # # # Never one to back from a challenge, I did it. A few mistakes & wrong turns, # # at times some strong language & threatening the computer with violence, a few # # questions to SAGE support groups, but, I finally pounded out code that # # worked. # # # # I named it "Hypotrochoid," as animating this was the original inspiration, # # but the code easily animates other graphs; see next: # # # # What this program does: # # # # This program animates (not just "draws," but "animates") graphs of several # # relations, the hypotrochoid, the hypocycloid, the limacon (or "limacon of # # Pascal"; SAGE doesn't like the French character in the original), the # # cardioid, the epitrochoid, and the epicycloid. # # # # The parametric equations that define a hypotrochoid follow; the parameter # # is *t*; for hypotrochoid we have R, r, d > 0, R > r > 0: # # # # x-coordinate: x = (R - r)*cos(t) + d*cos(((R - r)/r)*t) # # y-coordinate: y = (R - r)*sin(t) - d*sin(((R - r)/r)*t) # # # # For a epitrochoid, the equations are: # # # # x = (R + r)*cos(t) - d*cos(((R + r)/r)*t) # # y = (R + r)*sin(t) - d*sin(((R + r)/r)*t) # # # # The parametric equations are important in computing the period of the # # relation (below). # # # # These parametric equations live all over the Internet; Wikipedia has a # # reference for the hypochotroid: # # < http://en.wikipedia.org/wiki/Hypotrochoid >. # # See also < http://linuxgazette.net/133/luana.html > # # # # For the epitrochoid, see < http://en.wikipedia.org/wiki/Epitrochoid >. # # # # For the Limacon (SAGE completely choked and spewed error messages on the # # French character in the original, even in a comment) of Pascal, see # # < http://en.wikipedia.org/wiki/Lima%C3%A7on >. # # # # For the cardioid, see < http://en.wikipedia.org/wiki/Cardioid >. # # # # For the epitrochoid, see < < http://en.wikipedia.org/wiki/Epitrochoid > # # # # For the hypocycloid, see < http://en.wikipedia.org/wiki/Hypocycloid > # # # ################################################################################# ################################################################################# # A few further comments: Some curves of the "roulette" family (see # # < http://en.wikipedia.org/wiki/Roulette_%28curve%29 > may be easily # # animated with this program, as follows: # # # # 1) The epitrochoid (use the negative of "small" radius *r*, easy, puts # # rotating circle on the outside. # # 2) The hypocycloid, by setting 0 < r < R, d = r. # # 3) The limacon (or "limacon of Pascal"; SAGE doesn't like the French # # character in the original), use r < 0, R = abs(r). # # 4) The epicycloid, use r < 0 (put rotating circle on outside), d = r # # 5) The cardioid, r < 0 d = r # # # # There are others out there, like the Deltoid Curve, # # < http://en.wikipedia.org/wiki/Deltoid_curve >. # ################################################################################# ################################################################################# # To draw different graphs, vary that parameters *R* (fixed circle's radius), # # *r* (rotating circle's radius), and *d* (length of "bar" from rotating # # circle), below; other parameters may be tweaked at will. # ################################################################################# ################################################################################# # Of some note, the *epitrochoid* is the "epicycle" curve of Ptolemaic system # # astronomy; one project is to animate some of the Ptolemaic system, but this # # is for the future. # ################################################################################# ################################################################################# # # # Program commences; first a function to animate a graph: # # # ################################################################################# def animate_curve((g,f), bottom, top, step, x_min, x_max, y_min, y_max, fig_size = 5, curve_color = (0,0,1)): v = [] for i in srange(bottom, top+step, step): p = parametric_plot((g,f), 0, i, rgbcolor=curve_color) v.append(p) a = animate(v, xmin=x_min, xmax = x_max, ymin = y_min, ymax = y_max, figsize=[fig_size, fig_size]) return a ################################################################################# # # # Main program: # # # ################################################################################# # Parameters that define the image: R = 5 # Fixed circle's radius. r = 2 # Rotating circle's radius, rotates about fixed circle's radius; make # this negative to place rotating circle on outside. d = 4 # Length of the "bar" extending from the center of the rotating circle. step = 1.5*pi/30 # Size of "step" in below loops; the smaller the step, the more # "frames" in the final "movie" & the better the image, but the # slower the program runs -- and the more bytes to the image. figuresize = 4 # A constant, regulates size of final picture delayBetweenImages = 0 # A constant, delay between images in final "movie." colorOfFixedCircle = (0, 0, 1) # Colors of final image, colorOfRotatingCircle = (0, 1, 0) # all described by colorOfCenterPoint = (0, 0, 0) # names. colorOfBar = (0, 0, 0) colorOfCurve = (1, 0, 0) thicknessOfFixedCircle = 1 # Thickness of "fixed" circle that never moves. thicknessOfRotatingCircle = 1 # Thickness of circle that rotates. thicknessOfCenterOfRotatingCircle = 15 # Size of small circle's center. thicknessOfBar = 1 # Thickness of "bar" from rotating circle. penSize = 10 # Size of "pen" at end of the "bar." thicknessOfCurve = 0.5 # Thickness of final curve, really a lot of line segments. number_of_final_images = 10 # When the animation is over, the final image is # displayed a time. Probably will never vary; at bottom. # End of parameters user can realistically vary. g(x) = (R - r)*cos(x) + d*cos(((R - r)/r)*x) # Here define the f(x) = (R - r)*sin(x) - d*sin(((R - r)/r)*x) # curve for all time. origin = (0, 0) # Maybe irrelevant, but NO MAGIC NUMBERS!! See # < http://en.wikipedia.org/wiki/Magic_number_(programming) >. # Comes from my days as a C/C++ programmer. # More parameters, all of which are defined by earlier parameters: rDiff = (R-r) # The rotating circle may exceed fixed circle; # values may be negative, so we may have a # negative value. We use this so much, we give # it a name. sizeOfGraph = max(R, abs(r) + abs(d), abs(R) - (r) + abs(d)) # Big as it can get -- may be # a liberal estimate. rotations = (abs(rDiff)/r).denom() # Number of rotations about 2*pi. It # takes a bit of thought, but see the above # parametric equations. An example is best: picture R=8, r=6. We have # (rDiff)/r = (8-6)/6 = 2/6 = 1/3, reduced. For the argument *((rDiff)/r)t* to get back to 2*pi # (i.e., zero) in cos(((R - r)/r)t) & sin(((R - r)/r)t), we need to go 2*(3*pi). A pen & paper & some # scribbling makes this easy, be it this not clear. The *abs* is probably irrelevant. limit = ceil(2*rotations/step) # This many steps. # First: I use the same trig functions, over an over again. Why re-invent the # wheel & waste computer time? Put the needed trig functions in arrays & # "recycle" them as needed. sin1 = [ 0 for i in range(limit) ] # First sin2 = [ 0 for i in range(limit) ] # define cos1 = [ 0 for i in range(limit) ] # arrays, ... cos2 = [ 0 for i in range(limit) ] increment = 0 # Adding a bunch of numbers is more efficient than multiplying # *i*pi*step, but it is a trivial difference. multiplier = pi*rDiff/r # Efficiency! Don't repeat the same math # countless times. for i in srange(limit): # Note above definition of parameter *limit*, the # factor of 2, and the below trig functions. sin1[i] = sin(increment*pi) # Now fill cos1[i] = cos(increment*pi) # arrays sin2[i] = sin(increment*multiplier) # with our trig cos2[i] = cos(increment*multiplier) # functions. increment += step # End *i*-loop. # The next are described by names, but a few notes: # The parameter *fixedCircle* is merely a circle of radius *R* centered at # the origin. I animated it, as that makes it appear in all frames. It never # moves. # The parameter *rotatingCircle* is more computation-intense. It's center is on # the circle at *rDiff* from the edge of the fixed circle; its radius is *r*, and # it rotates about the fixed circle a total of *circle2PI* times. Find any internet # reference on these curves to see it. # The parameter *centerPoints* is solely to make the rotating circle # look nicer, a clear center. # The parameter *bar* is the "bar" extending from the center of the rotating # circle, that follows its rotation around the fixed circle. It always # emanates from the same center as *fixedCircle*, -- see *fixedCircle* # documentation, above -- and its terminal point comes from the parametric # equation of a hypotrochoid (top). # The parameter *pointAtPen* makes a point where the end of the bar draws # the figure, of the same color as the final curve. Note it is at the same # point as the terminal point of the "bar." fixedCircle = animate([circle(origin, R, rgbcolor=colorOfFixedCircle, thickness = thicknessOfFixedCircle) for i in srange(0, rotations*2*pi + step, step) ], xmin=-sizeOfGraph, ymin=-sizeOfGraph, xmax= sizeOfGraph, ymax= sizeOfGraph, figsize=[figuresize, figuresize]) rotatingCircle = animate([circle(((R-r)*cos(i), (R-r)*sin(i)), r, # Note center, radius *r* thickness = thicknessOfRotatingCircle, rgbcolor=colorOfRotatingCircle) for i in srange(0, rotations*2*pi + step, step) ], xmin=-sizeOfGraph, ymin=-sizeOfGraph, xmax= sizeOfGraph, ymax= sizeOfGraph, figsize=[figuresize,figuresize]) centerPoints = animate([point(((R-r)*cos(i), (R-r)*sin(i)), rgbcolor=colorOfCenterPoint, pointsize=thicknessOfCenterOfRotatingCircle) for i in srange(0, rotations*2*pi + step, step) ], xmin=-sizeOfGraph, ymin=-sizeOfGraph, xmax= sizeOfGraph, ymax= sizeOfGraph, figsize=[figuresize,figuresize]) bar = animate(line([((R-r)*cos(i), (R-r)*sin(i)), (g(i), f(i)) ], thickness = thicknessOfBar, rgbcolor=colorOfBar) for i in srange(0, rotations*2*pi + step, step)) pointAtPen = animate([point((g(i), f(i)), rgbcolor=colorOfCurve, pointsize=penSize) for i in srange(0, rotations*2*pi + step, step) ], xmin=-sizeOfGraph, ymin=-sizeOfGraph, xmax= sizeOfGraph, ymax= sizeOfGraph, figsize=[figuresize,figuresize]) # Now to animate the curve: animated_curve = animate_curve((g,f), 0, rotations*2*pi, step, -sizeOfGraph, sizeOfGraph, -sizeOfGraph, sizeOfGraph, figuresize, colorOfCurve) # Show the entire "movie": final_image = animate( [animated_curve[-1] for i in srange(0, number_of_final_images)], xmin=-sizeOfGraph, ymin=-sizeOfGraph, xmax= sizeOfGraph, ymax= sizeOfGraph, figsize=[figuresize,figuresize]) ((fixedCircle+rotatingCircle+pointAtPen+bar+centerPoints+animated_curve)*(final_image)).show(delay=delayBetweenImages) # We've shown the final image; done with program.