# # Define some macros for the globals # macro(CurveList=`Turtle/CurveList`); macro(PointList=`Turtle/PointList`); macro(Xpos=`Turtle/X`); macro(Ypos=`Turtle/Y`); macro(Heading=`Turtle/Heading`); macro(InitHeading=`Turtle/InitHeading`); macro(PenColor=`Turtle/PenColor`); macro(PenIsDown=`Turtle/PenIsDown`); macro(Angle=`Turtle/Angle`); macro(Length=`Turtle/Length`); macro(Scale=`Turtle/Scale`); macro(StateList=`Turtle/StateList`); # # Mod360 reduces its arg to be between 0 and 360. Mod360:= proc(angle::algebraic) local incr, ang; ang:=evalf(angle); if (ang < 0) then incr:= 360; elif (ang > 360) then incr:= -360; fi; while (ang < 0 or ang > 360) do ang := ang + incr; od; RETURN(ang); end: # # Setup and drawing routines Reset:= proc() global Length, Angle, Scale,InitHeading,StateList; Length:=1: Scale:=0.5: Angle:=90: InitHeading:=0; StateList:=[]; ClearPage(); NULL; end: ClearPage := proc() global CurveList, Xpos, Ypos, Heading, InitHeading, PenColor, PenIsDown, PointList; Xpos :=0: Ypos :=0: PenColor:=COLOUR(RGB,0,0,0): CurveList:=[]; PointList:=[]; PenIsDown:=true; SetHeading(InitHeading); PenDown(); NULL; end: ShowPath:=proc() global Curves; if (PenIsDown) then PenUp(); PenDown(); fi; PLOT( op(CurveList), AXESSTYLE(NONE), SCALING(CONSTRAINED)); end: # # Saving/Restoring Turtle's "State" # PushState:=proc() global Xpos, Ypos, Heading, InitHeading, PenColor, PenIsDown, PointList, Length, Angle, Scale, StateList; StateList:=[ [Xpos, Ypos, Heading, InitHeading, PenColor, PenIsDown, Length, Angle, Scale], op(StateList) ]; NULL; end: PopState:=proc() local i; global Xpos, Ypos, Heading, InitHeading, PenColor, PenIsDown, PointList, Length, Angle, Scale, StateList; if (nops(StateList) > 0) then Xpos :=StateList[1][1]; Ypos :=StateList[1][2]; Heading :=StateList[1][3]; InitHeading :=StateList[1][4]; PenColor :=StateList[1][5]; PenIsDown :=StateList[1][6]; Length :=StateList[1][7]; Angle :=StateList[1][8]; Scale :=StateList[1][9]; StateList :=[StateList[i] $i=2..nops(StateList)]; if (PenIsDown) then PenDown(); # just in case the pen changed state. fi; fi; NULL; end: # # Pen Manipulation primitives PenUp := proc() global PenIsDown,PointList,PenColor,CurveList; PenIsDown:=false; if (nops(PointList) > 1) then CurveList:= [op(CurveList), CURVES(PointList,PenColor)]; PointList:=[]; fi; NULL; end: PenDown := proc() global PenIsDown,Xpos,Ypos,PointList; if ((not PenIsDown) or (nops(PointList) = 0)) then PointList:=[[Xpos,Ypos]]; fi; PenIsDown:=true; NULL; end: SetPenColor:=proc(r::algebraic,g::algebraic,b::algebraic) global PenColor,PenIsDown; if (PenIsDown) then PenUp(); PenColor:=COLOUR(RGB,r,g,b); PenDown(); else PenColor:=COLOUR(RGB,r,g,b); fi; NULL; end: # # Low level motion primitives MoveAhead:= proc(dist::algebraic) local rads; global PenIsDown, PointList, Xpos, Ypos, Heading; rads:=evalf(Heading*Pi/180); Xpos:=evalf(Xpos + dist*cos(rads)); Ypos:=evalf(Ypos + dist*sin(rads)); if (PenIsDown) then PointList := [op(PointList), [Xpos,Ypos]]; fi; NULL; end: HeadTowards:= proc(x::algebraic, y::algebraic) local head; global Xpos, Ypos; if (evalf(x) = Xpos) then if (evalf(y) < Ypos) then SetHeading(-90); else SetHeading(90); fi; else head:=arctan((y-Ypos)/(x-Xpos))*180/Pi; if (evalf(y) > Ypos) then SetHeading(head); else SetHeading(180+head); fi; fi; NULL; end: MoveTo:= proc(x::algebraic, y::algebraic) global PenIsDown, PointList, Xpos, Ypos; HeadTowards(x,y); Xpos:=evalf(x); Ypos:=evalf(y); if (PenIsDown) then PointList := [op(PointList), [Xpos,Ypos]]; fi; NULL; end: # Setting of several globals: Heading, AngleIncremt, length increment, # scaling factor SetHeading:= proc(heading::algebraic) global Heading; Heading:=Mod360(heading); NULL; end: SetAngleIncr := proc(angle::algebraic) global Angle; Angle:=evalf(angle); NULL; end: SetLengthIncr:= proc(len::algebraic) global Length; Length:=evalf(len); NULL; end: SetScale:=proc(scale::algebraic) global Scale; Scale := scale; NULL; end: SetInitHeading:=proc(head::algebraic) global InitHeading; InitHeading:=head; NULL; end: # Higher-level motion and orientation primitives Forward:=proc() global Length; MoveAhead(Length); end: Back :=proc() global Length; MoveAhead(-Length); end: Left :=proc() global Heading, Angle; SetHeading(Heading+Angle); end: Right := proc() global Heading, Angle; SetHeading(Heading-Angle); end: Shrink:=proc() global Scale, Length; Length := evalf(Length*Scale); NULL; end: Grow := proc() global Scale, Length; Length := evalf(Length/Scale); NULL; end: # # Now remove the macro defs Reset(): # macro(CurveList=`CurveList`); macro(PointList=`PointList`); macro(Xpos=`Xpos`); macro(Ypos=`Ypos`); macro(Heading=`Heading`); macro(InitHeading=`InitHeading`); macro(PenColor=`PenColor`); macro(PenIsDown=`PenIsDown`); macro(Angle=`Angle`); macro(Length=`Length`); macro(Scale=`Scale`); # Stringy Stuff # DrawString := proc(cmd::name) local c, i; ClearPage(); for i from 1 to length(cmd) do c:=substring(cmd,i); DoCommand(c); od; ShowPath(); end: AnimateString := proc(cmd::name) global `Turtle/Length`, `Turtle/Scale`, `Turtle/Angle`,`Turtle/CurveList`; local i, j, c, len, s, a,frames; len:=`Turtle/Length`; s :=`Turtle/Scale`; a :=`Turtle/Angle`; frames:=[]; for i from 1 to length(cmd)+1 do ClearPage(); SetLengthIncr(len); SetScale(s); SetAngleIncr(a); for j from 1 to i-1 do c:=substring(cmd,j); DoCommand(c); od; if (i <=length(cmd) ) then SetPenColor(1,0,0); DoCommand(substring(cmd,i)); fi; PenUp(); frames:=[op(frames), PLOT(op(`Turtle/CurveList`),AXESSTYLE(NONE),SCALING(CONSTRAINED))]; od; plots[display](frames, insequence=true); end: DoCommand:=proc(c) if (c=`F`) then Forward(); elif (c=`B`) then Back(); elif (c=`L`) then Left(); elif (c=`R`) then Right(); elif (c=`S`) then Shrink(); elif (c=`G`) then Grow(); else print(`character`,c,`unrecognized...ignoring`); fi; NULL; end: