# # 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:= ()->ResetTurtle(): ResetTurtle:=proc() global Length, Angle, Scale,InitHeading,StateList,PenIsDown,PenColor; Length:=1: Scale:=0.5: Angle:=90: InitHeading:=0; StateList:=[]; PenIsDown:=true; PenColor:=COLOUR(RGB,0,0,0): PenDown(); ClearPage(); NULL; end: ClearPage := proc() global CurveList, Xpos, Ypos, Heading, InitHeading, PointList; Xpos :=0: Ypos :=0: CurveList:=[]; PointList:=[[Xpos,Ypos]]; SetHeading(InitHeading); NULL; end: TurtlePlotOptions:=axes=none,scaling=constrained: ShowPath:=proc() global CurveList; CommitTurtlePath(); if (nargs > 0) then plots[display](CurveList, args[1..nargs]); else plots[display](CurveList); fi; 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 CommitTurtlePath(); 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)]; fi; NULL; end: # transfer the current path from point list to the curve list CommitTurtlePath := proc() global PointList,PenColor,CurveList,Xpos,Ypos; if (nops(PointList) > 1) then CurveList:= [op(CurveList), CURVES(PointList,PenColor)]; PointList:=[[Xpos,Ypos]]; fi; NULL; end: # # Pen Manipulation primitives PenUp := proc() global PenIsDown,PointList,PenColor,CurveList; CommitTurtlePath(); PenIsDown:=false; 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 CommitTurtlePath(); fi; PenColor:=COLOUR(RGB,r,g,b); 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:=h->SetTurtleHeading(h): SetTurtleHeading:= proc(heading::algebraic) global Heading; Heading:=Mod360(heading); NULL; end: GetTurtleHeading:= proc() global Heading; Heading; end: SetAngleIncr:=ang->SetTurtleAngle(ang): SetTurtleAngle := proc(angle::algebraic) global Angle; Angle:=evalf(angle); NULL; end: GetTurtleAngle := proc() global Angle; Angle; end: SetLengthIncr:= l->SetTurtleStepsize(l): SetTurtleStepsize:= proc(len::algebraic) global Length; Length:=evalf(len); NULL; end: GetTurtleStepsize := proc() global Length; Length; end: SetScale:=s->SetTurtleScale(s): SetTurtleScale:=proc(scale::algebraic) global Scale; Scale := scale; NULL; end: GetTurtleScale:=proc() global Scale; Scale; end: SetInitHeading:=h->SetInitialTurtleHeading(h): SetInitialTurtleHeading:=proc(head::algebraic) global InitHeading; InitHeading:=head; NULL; end: GetInitialTurtleHeading:=proc() global InitHeading; InitHeading; 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 # TurtleCmd := proc(cmd) local c, i, j,m, n, plotopts; ClearPage(); if (nargs>1) then plotopts := TurtlePlotOptions,args[2..nargs]; else plotopts := TurtlePlotOptions; fi; n:=0; for i from 1 to length(cmd) do c:=substring(cmd,i); if (searchtext(c,"0123456789") >0 ) then n := 10*n + parse(c,statement); else if (n>0) then for j from 1 to n do DoCommand(c); od; n:=0; else DoCommand(c); fi; fi; od; ShowPath(plotopts); end: DrawString := s -> TurtleCmd(s): AnimateString := s-> AnimateTurtleCmd(s): AnimateTurtleCmd := 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(); SetPenColor(0,0,0); SetTurtleStepsize(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,scaling=constrained); end: DoCommand:=proc(cmd) local c; c:=convert(cmd,name); # just in case we're given a string 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(); elif (c=` `) then NULL; elif (c=`U`) then PenUp(); elif (c=`D`) then PenDown(); elif (c=`w`) then SetPenColor(1,1,1); elif (c=`k`) then SetPenColor(0,0,0); elif (c=`r`) then SetPenColor(1,0,0); elif (c=`g`) then SetPenColor(0,1,0); elif (c=`b`) then SetPenColor(0,0,1); elif (c=`v`) then PushState(); elif (c=`^`) then PopState(); elif (c=`[`) then PushState(); SetTurtleAngle(90); elif (c=`]`) then PopState(); else if (not DoUserCommand(c)) then WARNING(sprintf("Unknown turtle command %c encountered",c)); fi; fi; NULL; end: DoUserCommand:=c->false: