module turtle;

% REDUCE implementation of Turtle Graphics

% Caroline Cotter, ZIB,Berlin, 1998.

% The main user commands for drawing pictures follow.

load_package gnuplot;

%USER SETTING FUNCTIONS
% The following allow the user to reset the position of the turtle whilst
% drawing a %picture - that is, these commands are to be used when the pen is
% to be lifted and set down at a new point, or to change the direction
% (heading) of the turtle. Since these do not actually draw anything,
% nothing is returned. However, in order %to allow a continuous drawing,
% the latest  position of the turtle must be returned
%(otherwise an error is incurred when trying to plot the points).

algebraic<<

procedure setheading(mu);             %Redirects the turtle
begin scalar w;
      w:=if mu=() then heading
          else ck(mu);                %remember ck sets between 0 and 360
      heading:=w;
      return {x_coord,y_coord} 
end;


procedure setx(i);		   %Relocates the turtle in the x-direction
begin;
      x_coord:=i;
      return {x_coord,y_coord} 
end;


procedure sety(j);                 %Relocates the turtle in the y-direction
begin;
      y_coord:=j;
      return {x_coord,y_coord}
end;


procedure setposition(q);             %Repositions the turtle (takes cartesian
begin;                                %coordinate as its argument)
      x_coord:=first q;
      y_coord:=second q;
      return {x_coord,y_coord} 
end;


% Both turnleft and turnright redirect the turtle,
% dependent on its current direction

procedure turnleft(gamma);            
begin;
      heading:=ck(heading + gamma); 
      return {x_coord,y_coord} 
end;


procedure turnright(delta);
begin;
      heading:=ck(heading - delta);
      return {x_coord,y_coord} 
end;


procedure setheadingtowards(q);   %This takes a cartesian coordinate point as
                                  %its argument and redirects the turtle towards
begin scalar x,y,f;               %the point specified.
      x:=first q - x_coord;
      y:=second q - y_coord;
      f:=polar({x,y});
      heading:= ck(second f);
      return {x_coord,y_coord} 
end;


%We also need to use forward/back without drawing a line
%(in addition to the other set commands). These have the effect
% of penup/pendown commands used in conjunction with forward/back.


procedure setforward(m);
begin scalar theta,s,u;
      theta:=heading;
      s:={m,theta};
      u:=cartesian(s);
      return setposition(u) 
end;


procedure setback(n);
begin scalar theta,v,w;
      theta:=ck(heading+180);
      v:={n,theta};
      w:=cartesian(v);
      heading:=ck(theta-180);
      return setposition(w)       
end;


%LINE-DRAWING FUNCTIONS
% The following functions are used when an actual line is to be drawn
% between two points on the graph. They each return a list of two points which,
% when used within the draw function, are joined together by a line.
% In addition, they reset the position of the turtle,
% but do not alter the direction stored.


procedure move(p);                %This takes a cartesian coordinate point as 
                                  %its argument and draws a line towards the
begin scalar x,y,line;            %specified point
      x:=first p;
      y:=second p;
      line:={{x_coord,y_coord},{x,y}};
      x_coord:=x;
      y_coord:=y;
      return line 
end;


procedure forward(c);           %The turtle is moved c units in the direction
begin scalar theta,s,u,fl;      %of the current heading setting
      theta:=heading;
      s:={c,theta};
      u:=cartesian(s);
     fl:= {{x_coord,y_coord},{x_coord+first(u),y_coord+second(u)}};
      x_coord:=x_coord+first(u);
      y_coord:=y_coord+second(u);
      return fl 
end;


procedure back(d);               %The turtle is moved d units in the opposite
begin scalar theta,v,w,bl;       %direction to heading
      theta:=ck(heading+180);
      v:={d,theta};
      w:=cartesian(v);
     bl:= {{x_coord,y_coord},{x_coord+first(w),y_coord+second(w)}};
      x_coord:=x_coord+first(w);
      y_coord:=y_coord+second(w);
      heading:=ck(theta-180);
      return bl
end;



%PLOTTING PICTURES
% The next functions gather the commands input by the user and turn
% them into a graph output in a gnuplot window;


procedure draw(p);             %This is the function the user calls to draw
                               %the list of commands as a picture. It takes  
begin scalar g;                %a list as its argument. The items in the list 
                               %are expected to be any of the setting or plot-
				%ting functions already outlined.
      g:=for each a in p collect a;
      plot g 
end;


%SUMMARY
% The main variables:
%           x_coord
%           y_coord
%           heading
%are global, so it is advised that these are not altered directly.

% The following functions have been used in order to create the user commands, but they 
%cannot be used directly in the draw function:
%           degree, rad,
%           polar, cartesian,
%           ck, try.
%(also the info command is designed to be used outside of a call to draw)

% The following functions are all user commands which can be placed in the list to be 
%executed by the draw command:
%           clearscreen
%           home
%           setheading
%           setx
%           sety
%           setposition
%           turnleft
%           turnright
%           setheadingtowards
%           setforward
%           setback
%           move
%           forward
%           back           

% The most important function is the draw function. It takes the list
% of commands and plots the points given.

%NOTE
% When using conditional statements under a call to draw, the final else
% statement must return a point or at least {x_coord,y_coord} if the picture
% is to be continued. Also for statements must include 'collect ' with a
% list of drawing commands. (The variable needs to begin counting from 0
% if it is to be joined onto the previous list %of drawing commands,
% e.g. for i:=0:10 collect{.......}).


% This program is designed to take the "Turtle Graphics" commands and implement
% them in REDUCE. 
% Where possible, commands have remained the same, but pen-up & pen-down
% commands are not used. Instead the commands either set the variables
%(which are the x and y coordinates and heading variable), or draw a line.


%STARTING UP
% Many commands have either ordinary cartesian arguments or polar coordinate
% arguments, so we need to be able to transform them all into cartesian to
% plot on an x-y plane. This invovles the use of the pi function, so we
% need floating point accuracy:

%% on rounded;

% The following are the main variables of the program, but the user should not 
%attempt to alter them directly. The functions
% 'setx,sety,setposition,setheading' are for that purpose.

x_coord:=0;
y_coord:=0;
heading:=0;

procedure clearscreen();           % This function resets the variables to the 
begin;                             %original position. 
      plotreset;                   %If the user has plotkeep on then this will
                                   %clear the current gnuplot window.
      x_coord:=0;
      y_coord:=0;
      heading:=0;
      return {x_coord,y_coord} 
end;


procedure home();              % This also resets the variables and in general 
begin;                         %is sufficient since gnuplot automatically rep-
      x_coord:=0;              %laces its windows each time it plots a new 
      y_coord:=0;              %graph.
      heading:=0;
      return {x_coord,y_coord} 
end;



%DEGREE-RADIAN TRANSFORMS
% These functions are called in the commands for drawing graphs.
% The user need not call on either degree or rad for drawing purposes.


procedure degree(theta); 
begin scalar a;                  
      a:=theta*180/pi;
      return a 
end;


procedure rad(mu);        
begin scalar b;
      b:=mu*pi/180;
      return b 
end;



%POLAR-CARTESIAN TRANSFORMS
% Again, there is no use for these functions in drawing, but they are needed to 
% turn user inputs into x-y coordinate points.


procedure polar(p);
begin scalar x,y,r,theta;
      x:=first p;
      y:=second p;
      r:=(x^2+y^2)^(1/2);
      if x>0 then theta:=atan(y/x)
             else if x=0 then theta:=sign(y)*pi/2
                         else theta:=pi+atan(y/x);
      return(list(r,degree(theta))) 
end;


procedure cartesian(p);
begin scalar r,theta,x,y;
      r:=first p;
      theta:=rad(second p);
      x:=r*cos(theta);
      y:=r*sin(theta);
      return(list(x,y)) 
end;


procedure ck(m);                %This is a useful function to keep the heading 
                                %variable within the 0-360 range. It is used 
begin;                          %within other functions for controlling the 
                                %heading size - it is not necessary for the 
                                %user to call ck.
      if numberp m then <<
                   if (m>=0 and m<360) then heading:=m;
                   if (m<0) then ck(360+m);
                   if (m>=360) then ck(m-360) >>
      else rederr "error:ck needs numeric argument";
      return heading 
end;

>>;

endmodule;

end;

