Simple turtle graphics
You are encouraged to solve this task according to the task description, using any language you may know.
The first turtle graphic discussed in Mindstorms: Children, Computers, and Powerful Ideas by Seymour Papert is a simple drawing of a house. It is a square with a triangle on top for the roof.
For a slightly more advanced audience, a more practical introduction to turtle graphics might be to draw a bar chart.
- Task
-
- Create a function (or subroutine) that uses turtle graphics to draw a house of a specified size as depicted. Optionally make it lovely by adding details such as, for example, doors and windows.
- Create a function (or subroutine) that takes a list (array, vector) of non-negative numbers and draws a bar chart from them, scaled to fit exactly in a square of a specified size. The enclosing square need not be drawn.
- Both functions should return the turtle to the location it was at and facing in the same direction as it was immediately before the function was executed.
Action!
INCLUDE "D2:TURTLE.ACT" ;from the Action! Tool Kit
PROC Rectangle(INT w,h)
BYTE i
FOR i=1 TO 2
DO
Forward(h)
Left(90)
Forward(w)
Left(90)
OD
RETURN
PROC Square(INT w)
Rectangle(w,w)
RETURN
PROC Triangle(INT w)
BYTE i
FOR i=1 TO 3
DO
Forward(w)
Right(120)
OD
RETURN
PROC House(INT w)
Left(90)
Square(w)
Triangle(w)
Right(90)
RETURN
INT FUNC GetMax(INT ARRAY a INT count)
INT i,max
max=0
FOR i=0 TO count-1
DO
IF a(i)>max THEN
max=a(i)
FI
OD
RETURN (max)
PROC BarChart(INT ARRAY a INT count,w)
INT max,st,i
IF count=0 THEN RETURN FI
max=GetMax(a,count)
st=w/count
Right(90)
FOR i=0 TO count-1
DO
Rectangle(a(i)*w/max,st)
Forward(st)
OD
Left(180)
Forward(w)
RETURN
PROC Main()
BYTE CH=$02FC,COLOR1=$02C5,COLOR2=$02C6
INT ARRAY a=[50 33 200 130 50]
Graphics(8+16)
COLOR1=$0C
COLOR2=$02
Color=1
SetTurtle(150,110,90)
House(75)
Color=0
Right(90)
Forward(5)
Left(90)
Color=1
BarChart(a,5,100)
Right(90)
Forward(5)
Right(90)
DO UNTIL CH#$FF OD
CH=$FF
RETURN
- Output:
Screenshot from Atari 8-bit computer
Ada
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Characters; use Ada.Characters;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Ada.Strings; use Ada.Strings;
-- procedure main - begins program execution
procedure main is
type Sketch_Pad is array(1 .. 50, 1 .. 50) of Character;
thePen : Boolean := True; -- pen raised by default
sketch : Sketch_Pad;
ycorr, xcorr : Integer := 25;
-- specifications
function penPosition(thePen : in out Boolean) return String;
procedure initGrid(sketch : in out Sketch_Pad);
procedure commandMenu(thePen : in out Boolean; xcorr : in out Integer;
ycorr : in out Integer);
procedure showMenu(xcorr : in out Integer; ycorr : in out Integer;
thePen : in out Boolean; sketch : in Sketch_Pad);
procedure moveCursor(thePen : in Boolean; sketch : in out Sketch_Pad;
xcorr : in out Integer; ycorr : in out Integer;
ch : in Integer);
procedure showGrid(sketch : in Sketch_Pad);
-- procedure initGrid - creates the sketchpad and initializes elements
procedure initGrid(sketch : in out Sketch_Pad) is
begin
sketch := (others => (others => ' '));
end initGrid;
-- procedure showMenu - displays the menu for the application
procedure showMenu(xcorr : in out Integer; ycorr : in out Integer;
thePen : in out Boolean; sketch : in Sketch_Pad) is
choice : Integer := 0;
begin
while choice /= 4 loop
Set_Col(15);
Put("TURTLE GRAPHICS APPLICATION");
Set_Col(15);
Put("===========================");
New_Line(2);
Put_Line("Enter 1 to print the grid map");
Put_Line("Enter 2 for command menu");
Put_Line("Enter 3 to raise pen up / down");
Put_Line("Enter 4 to exit the application");
choice := integer'value(Get_Line);
exit when choice = 4;
case choice is
when 1 => showGrid(sketch);
when 2 => commandMenu(thePen, xcorr, ycorr);
when 3 => Put_Line("Pen is "
& penPosition(thePen));
when others => Put_Line("Invalid input");
end case;
end loop;
end showMenu;
-- function penPosition - checks changes the state of whether the pen is
-- raised up or down. If value is True, pen is rasied up
function penPosition(thePen : in out Boolean) return String is
str1 : constant String := "raised UP";
str2 : constant String := "raised DOWN";
begin
if thePen = True then
thePen := False;
return str2;
else
thePen := True;
end if;
return str1;
end penPosition;
-- procedure command menu - provides a list of directions for the turtle
-- to move along the grid
procedure commandMenu(thePen : in out Boolean; xcorr : in out Integer;
ycorr : in out Integer) is
choice : Integer := 0;
begin
while choice <= 0 or choice > 5 loop
Set_Col(15);
Put("Command Menu");
Set_Col(15);
Put("============");
New_Line(2);
Put_Line("To move North enter 1");
Put_Line("To move South enter 2");
Put_Line("To move East enter 3");
Put_Line("To move West enter 4");
Put_Line("To return to previous menu enter 5");
choice := integer'value(Get_Line);
case choice is
when 1 => moveCursor(thePen, sketch, xcorr, ycorr, choice);
when 2 => moveCursor(thePen, sketch, xcorr, ycorr, choice);
when 3 => moveCursor(thePen, sketch, xcorr, ycorr, choice);
when 4 => moveCursor(thePen, sketch, xcorr, ycorr, choice);
when 5 => showMenu(xcorr, ycorr, thePen, sketch);
when others => Put_Line("Invalid choice");
end case;
end loop;
end commandMenu;
-- procedure moveCursor - moves the cursor around the board by taking the
-- x and y coordinates from the user. If the pen is down, a character is
-- printed at that location. If the pen is up, nothing is printed but the
-- cursor still moves to that position
procedure moveCursor(thePen : in Boolean; sketch : in out Sketch_Pad;
xcorr : in out Integer; ycorr : in out Integer;
ch : in Integer) is
begin
if thePen = True then -- pen up so move cursor but do not draw
case ch is
when 1 => xcorr := xcorr - 1; ycorr := ycorr;
sketch(xcorr, ycorr) := ' ';
when 2 => xcorr := xcorr + 1; ycorr := ycorr;
sketch(xcorr, ycorr) := ' ';
when 3 => xcorr := xcorr; ycorr := ycorr + 1;
sketch(xcorr, ycorr) := ' ';
when 4 => xcorr := xcorr; ycorr := ycorr - 1;
sketch(xcorr, ycorr) := ' ';
when others => Put("Unreachable Code");
end case;
else -- pen is down so move cursor and draw
case ch is
when 1 => xcorr := xcorr - 1; ycorr := ycorr;
sketch(xcorr, ycorr) := '#';
when 2 => xcorr := xcorr + 1; ycorr := ycorr;
sketch(xcorr, ycorr) := '#';
when 3 => xcorr := xcorr; ycorr := ycorr + 1;
sketch(xcorr, ycorr) := '#';
when 4 => xcorr := xcorr; ycorr := ycorr - 1;
sketch(xcorr, ycorr) := '#';
when others => Put("Unreachable Code");
end case;
end if;
end moveCursor;
-- procedure showGrid - prints the sketchpad showing the plotted moves
procedure showGrid(sketch : in Sketch_Pad) is
begin
New_Line;
for I in sketch'Range(1) loop
for J in sketch'Range(2) loop
Put(character'image(sketch(I,J)));
end loop;
New_Line;
end loop;
New_Line;
end showGrid;
begin
New_Line;
initGrid(sketch);
showMenu(xcorr, ycorr, thePen, sketch);
New_Line;
end main;
EasyLang
subr home
deg = 0
x = 50
y = 50
down = 0
move x y
.
home
#
proc forward n . .
x += cos deg * n
y += sin deg * n
if down = 1
line x y
else
move x y
.
sleep 0.1
.
proc turn a . .
deg -= a
.
#
proc house . .
turn 180
forward 45
turn 180
down = 1
#
forward 30
turn 90
forward 30
turn 90
forward 30
turn 90
forward 30
#
turn 30
forward 30
turn 120
forward 30
home
.
house
#
proc bar a[] . .
turn 90
forward 30
turn -90
down = 1
for i to len a[]
max = higher max a[i]
.
for i to len a[]
h = a[i] / max * 50
w = 45 / len a[]
turn -90
forward h
turn 90
forward w
turn 90
forward h
turn -90
.
turn 180
forward 45
home
.
bar [ 50 33 200 130 50 ]
FreeBASIC
Library: Turtle_Graphics.bi
' Turtle Graphics Library by Angelo Rosina 1995
' Ported to Freebasic by Angelo Rosina 2007
'
'(RIGH and LEF because RIGHT and LEFT are reserved keywords, of course)
Declare Sub LEF (gradi As Integer)
Declare Sub SETCOL (C As Integer)
Declare Sub SETSIZE (C As Integer)
Declare Sub RIGH (gradi As Integer)
Declare Sub FORW (passi As Integer)
Declare Sub BACK (passi As Integer)
Declare Sub TURTLE (numero As Integer)
Declare Sub UP ()
Declare Sub DOWN ()
Declare Sub DTurtle ()
Dim Shared As Integer graph, dimen, angulo, N, dis, su, pasos
Dim Shared As Integer colore(10), figura(5000,10), figura2()
Dim Shared As Long X(10), Y(10)
Dim Shared As String comando
Sub BACK (passi As Integer)
pasos = passi
comando = "d " & Str(passi)
DTurtle
comando = ""
pasos = 0
End Sub
Sub DOWN
su = 0
End Sub
Sub DTurtle Static
Dim As Integer rifa, lavora, XX, YY, scher(), FX, FY
Dim As String R
If rifa = 0 Then rifa = 1: Redim scher(5000)
If lavora Then Put (XX - dimen * 4, YY - dimen * 2), scher, Pset
'SCREEN graph
If su Then comando = "b" & comando
FX = X(N)
FY = Y(N)
R = "s" & Str(dimen) & "bm" & Str(FX) & "," & Str(FY) & "c" & Str(colore(N)) & "Ta" & Str(angulo) & comando
Draw R
X(N) = X(N) + Sin(angulo / 57.2957) * dimen / 4 * pasos
Y(N) = Y(N) + Cos(angulo / 57.2957) * dimen / 4 * pasos
XX = X(N)
YY = Y(N)
Get (X(N) - dimen * 4, Y(N) - dimen * 2)-(X(N) + dimen * 4, Y(N) + dimen * 2), scher: lavora = 1
If dis = 0 Then
R = "bm" & Str(XX) & "," & Str(YY) & "Ta" & Str(angulo) & "r1 ta " & Str(angulo + 15 + 360 * (angulo + 15 > 360)) & "u7 ta " & Str(angulo + 165 + 360 * (angulo + 165 > 360)) & "u7 ta" & Str(angulo) & " m" & Str(XX) & "," & Str(YY)
Draw R
Else
Put (XX, YY), figura2, Or
End If
End Sub
Sub FORW (passi As Integer)
pasos = -passi
comando = "u " & Str(passi)
DTurtle
comando = ""
pasos = 0
End Sub
Sub LEF (gradi As Integer)
angulo += gradi
Do Until angulo <= 360
angulo -= 360
Loop
DTurtle
End Sub
Sub RIGH (gradi As Integer)
angulo -= gradi
Do Until angulo >= 0
angulo += 360
Loop
DTurtle
End Sub
Sub SETCOL (C As Integer)
colore(N) = C
DTurtle
End Sub
Sub SETSIZE (C As Integer)
dimen = C
DTurtle
End Sub
Sub TURTLE (numero As Integer) Static
Dim As Integer i, NN, j
Dim As Integer ang()
Redim figura2(dis)
If dis <> 0 And graph <> 0 Then
For i = 0 To dis
figura2(i) = figura(i, numero)
Next i
End If
NN = N
N = numero
If j = 0 Then j = 1: Redim ang(10): dimen = 4
If X(numero) = 0 Then X(numero) = 320
If Y(numero) = 0 Then Y(numero) = 100: colore(numero) = 15
ang(NN) = angulo
angulo = ang(N)
DTurtle
End Sub
Sub UP
su = 1
End Sub
Turtle test:
'Rosetta Code problem: https://rosettacode.org/wiki/Simple_turtle_graphics
'by Jjuanhdez, 03/2023
#include "Turtle_Graphics.bi"
Sub rectangle(ancho As Short, alto As Short)
For i As Short = 1 To 2
FORW alto
LEF 90
FORW ancho
LEF 90
Next i
End Sub
Sub square(lado As Short)
rectangle(lado, lado)
End Sub
Sub triangle(lado As Short)
For i As Short = 1 To 3
FORW lado
RIGH 120
Next i
End Sub
Sub house(lado As Short)
LEF 90
square lado
triangle lado
RIGH 90
End Sub
Sub barchart(lst() As Double, lado As Short)
Dim As Short i, min, max
max = lst(Ubound(lst))
For i = Lbound(lst) To Ubound(lst)
If lst(i) > max Then max = lst(i)
Next i
Dim As Short escala = lado/max
Dim As Short ancho = lado/Ubound(lst)
For i = Lbound(lst) To Ubound(lst)
rectangle lst(i), ancho
UP
FORW ancho
DOWN
Next i
UP
BACK lado
DOWN
End Sub
Screenres 800,600
Windowtitle "Simple turtle graphics"
Cls
TURTLE 1
SETSIZE 3
SETCOL 0 'black
DOWN
BACK 200
SETCOL 2 'green
house 150
UP
RIGH 90
FORW 50
Dim As Double s(1 To 6) = {1, 50, 33, 200, 130, 50}
barchart s(), 200
SETCOL 0
Sleep
- Output:
Simple turtle graphics FreeBasic image
IS-BASIC
100 PROGRAM "Turtle.bas"
110 OPTION ANGLE DEGREES
120 GRAPHICS HIRES 4
130 CALL HOUSE(200,200,200)
140 SET INK 2:CALL CHART(450,200,1000,700)
150 DEF HOUSE(X,Y,L)
160 PLOT X,Y,ANGLE 0;
170 FOR I=1 TO 4
180 PLOT FORWARD 200,RIGHT 90;
190 NEXT
200 PLOT LEFT 60;FORWARD L;RIGHT 120;FORWARD L;X,Y,
210 END DEF
220 DEF CHART(X,Y,X1,Y1)
230 LET PC,MX=0
240 DO
250 READ IF MISSING EXIT DO:Z
260 LET PC=PC+1:LET MX=MAX(MX,Z)
270 LOOP
280 RESTORE
290 LET L=(X1-X)/PC:LET MX=(Y1-Y)/MX
300 PLOT X,Y,
310 DO
320 READ IF MISSING EXIT DO:Z
330 PLOT ANGLE 90;FORWARD Z*MX;RIGHT 90;FORWARD L;RIGHT 90;FORWARD Z*MX;RIGHT 90;FORWARD L
340 PLOT ANGLE 0;FORWARD L
350 LOOP
360 PLOT X,Y,
370 END DEF
380 DATA 90,60,300,200,90
J
Prerequisites (requires a network connection or, for a non-networked system, significant technical savvy), and assumes user is running the a recent version of J's qtide (perhaps J 9.3):
;install each cut 'gl2 gles github:zerowords/tgsjo'
Implementation (note that this is meant to be copied and pasted to a file which J will load, rather than being typed in line by line -- manual typing would work if no errors were made, but that would be a painstaking approach):
load'zerowords/tgsjo'
rotR 0 0 _90
translate 0 0 _40
clearscreen ''
createTurtle 0 0 0
rectangle=: {{
2 repeats {{ 'width height'=. y
forward height
left 90
forward width
left 90 }} y
}}
square=: {{size=. y
rectangle size,size
}}
triangle=:{{
3 repeats {{size=. y
forward size
right 120}} y
}}
house=:{{size=. y
left 90
square size
triangle size
right 90
}}
barchart=: {{'lst size'=. y
if.#lst do.
scale=. size%>./lst
width=. size%#lst
right 90
for_j. lst do.
rectangle (j * scale),width
forward width
end.
back size
left 90
end.
}}
penColor Red
house 150
pen 0
right 90
forward 10
left 90
pen 1 [ penColor Blue
barchart 0.5 0.3333 2 1.3 0.5; 200
pen 0
left 90
forward 10
left 270
pen 1
Opens a window with the indicated content. House is red, barchart is blue.
Note that we have used the Logo naming convention, which means that the height of our barchart is the width parameter in rectangle (and, likewise, the width of each bar is the height parameter in rectangle)
Java
Opens a javax.swing.JFrame, displaying the graphics shown in the screenshot:
/* Class Turtle starts here */
import java.awt.geom.AffineTransform;
public class Turtle extends java.lang.Object
{
private final java.awt.Graphics2D origin;
private java.awt.Graphics2D g2;
public Turtle(java.awt.Graphics2D origin)
{
this.origin = origin;
origin();
}
public void origin()
{
g2=(java.awt.Graphics2D)origin.create();
}
public void relativePosition(int xoff, int yoff)
{
AffineTransform at=g2.getTransform();
at.concatenate(AffineTransform.
getTranslateInstance(xoff,yoff));
g2.setTransform(at);
}
public void turnByDegrees(int thetaInDegrees)
{
AffineTransform at=g2.getTransform();
at.concatenate(AffineTransform.
getRotateInstance(Math.toRadians(thetaInDegrees)));
g2.setTransform(at);
}
public void forward(int len)
{
g2.drawLine(0,0,len,0);
relativePosition(len,0);
}
}
/* Class CanvasComponent starts here*/
import java.awt.Graphics;
import java.awt.Graphics2D;
public class CanvasComponent extends javax.swing.JComponent
{
protected void paintComponent(Graphics g)
{
Turtle turtle=new Turtle((Graphics2D)g);
turtle.origin();
turtle.relativePosition(50,50);
house(turtle,100,200,50);
turtle.origin();
turtle.relativePosition(200,50);
double[] numbers=new double[]{0.5, 0.33333, 2, 1.3, 0.5};
barchart(turtle,200,numbers);
}
private void barchart(Turtle turtle,int size,double[] numbers)
{
double max=0;
for(double d:numbers)
{
if(d>max)
max=d;
}
double width=size/ numbers.length;
int xpos=400;
for(double d:numbers)
{
int h=(int) (size * (d / max));
rectangle(turtle, (int)width, h);
xpos+=width;
turtle.relativePosition((int)width,0);
}
}
private void house(Turtle turtle,int width,
int height, int roofheight)
{
rectangle(turtle,width,height);
turtle.relativePosition(0,height);
double dist= Math.sqrt(roofheight*roofheight+width/2*width/2);
double angle= Math.toDegrees(Math.asin(roofheight/dist));
turtle.turnByDegrees((int)angle);
turtle.forward((int)dist);
turtle.turnByDegrees(-2*(int)angle);
turtle.forward((int)dist);
}
private void rectangle(Turtle turtle,int width, int height)
{
for(int i=0;i<2;++i)
{
turtle.forward(width);
turtle.turnByDegrees(90);
turtle.forward(height);
turtle.turnByDegrees(90);
}
}
}
/* Class MainClass starts here */
import javax.swing.*;
import java.awt.*;
public class MainClass
{
public static void main(String[] args)
{
CanvasComponent canvas=new CanvasComponent();
canvas.setPreferredSize(new Dimension(800,600));
JFrame f=new JFrame();
JPanel p=new JPanel(new BorderLayout());
p.add(canvas);
f.setContentPane(p);
f.setDefaultCloseOperation(f.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
}
}
Julia
Outputs a PNG file.
using Luxor, Colors
function house(🐢, x, y, siz)
oldorientation = 🐢.orientation
xpos, ypos = 🐢.xpos, 🐢.ypos
# house wall
Reposition(🐢, x, y)
Rectangle(🐢, siz, siz)
# roof
Reposition(🐢, x - siz / 2, y - siz / 2)
Turn(🐢, -60)
Forward(🐢, siz)
Turn(🐢, 120)
Forward(🐢, siz)
# turtle_demo
doorheight, doorwidth = siz / 2, siz / 4
Pencolor(🐢, 0, 0, 0)
Reposition(🐢, x, y + doorheight / 2)
Rectangle(🐢, doorwidth, doorheight)
# window
windowheight, windowwidth = siz /3, siz / 4
Reposition(🐢, x + siz / 4, y - siz / 4)
Rectangle(🐢, windowwidth, windowheight)
Reposition(🐢, x - siz / 4, y - siz / 4)
Rectangle(🐢, windowwidth, windowheight)
Orientation(🐢, oldorientation)
Reposition(🐢, xpos, ypos)
end
function barchart(🐢, data, x, y, siz)
oldorientation = 🐢.orientation
xpos, ypos = 🐢.xpos, 🐢.ypos
maxdata = maximum(data)
# scale to fit within a square with sides `siz` and draw bars of chart
barwidth = siz / length(data)
Pencolor(🐢, 1.0, 0.0, 0.5)
Reposition(🐢, x, y)
for n in data # draw each bar in chart
barheight = n * siz / maxdata
Reposition(🐢, x, y - barheight / 2)
Rectangle(🐢, barwidth, barheight)
x += barwidth
end
Orientation(🐢, oldorientation)
Reposition(🐢, xpos, ypos)
end
function testturtle(width = 400, height = 600)
dra = Drawing(600, 400, "turtle_demo.png")
origin()
background("midnightblue")
🐢 = Turtle()
Pencolor(🐢, "cyan")
Penwidth(🐢, 1.5)
house(🐢, -width / 3, height / 7, width / 2)
barchart(🐢, [15, 10, 50, 35, 20], width / 8, height / 8, width / 2)
finish()
end
testturtle()
Logo
to rectangle :width :height
repeat 2 [
forward :height
left 90
forward :width
left 90 ]
end
to square :size
rectangle size size
end
to triangle :size
repeat 3 [
forward size
right 120 ]
end
to house :size
left 90
square size
triangle size
right 90
end
to max :lst
if equalp count lst 1 [ output first lst ]
make "x max butfirst lst
if x > first lst [ output x ]
output first lst
end
to barchart :lst :size
right 90
if emptyp lst [ stop ]
make "scale size / (max lst)
make "width size / count lst
foreach lst [
rectangle ? * scale width
forward width ]
back size
left 90
end
clearscreen hideturtle
house 150
penup
right 90 forward 10 left 90
pendown
barchart [ 0.5 0.33333 2 1.3 0.5 ] 200
left 90 back 10 right 90
- Output:
Perl
Added octangle window to house attic.
#!/usr/bin/perl
use strict; # https://rosettacode.org/wiki/Simple_turtle_graphics
use warnings;
use Tk;
use List::Util qw( max );
my $c; # the canvas
# turtle routines
my $pen = 1; # true for pendown, false for penup
my @location = (0, 0); # upper left corner
my $direction = 0; # 0 for East, increasing clockwise
my @stack;
my $radian = 180 / atan2 0, -1;
sub dsin { sin $_[0] / $radian }
sub dcos { cos $_[0] / $radian }
sub save { push @stack, [ $direction, @location ] }
sub restore { ($direction, @location) = @{ pop @stack } }
sub turn { $direction += shift }
sub right { turn shift }
sub left { turn -shift }
sub forward
{
my $x = $location[0] + $_[0] * dcos $direction;
my $y = $location[1] + $_[0] * dsin $direction;
$pen and $c->createLine( @location, $x, $y, -width => 3 );
@location = ($x, $y);
}
sub back { turn 180; forward shift; turn 180 }
sub penup { $pen = 0 }
sub pendown { $pen = 1 }
sub text { $c->createText( @location, -text => shift ) }
# make window
my $mw = MainWindow->new;
$c = $mw->Canvas(
-width => 900, -height => 900,
)->pack;
$mw->Button(-text => 'Exit', -command => sub {$mw->destroy},
)->pack(-fill => 'x');
$mw->after(0, \&run);
MainLoop;
-M $0 < 0 and exec $0;
sub box
{
my ($w, $h) = @_;
for (1 .. 2)
{
forward $w;
left 90;
forward $h;
left 90;
}
}
sub house
{
my $size = shift;
box $size, $size;
right 90;
for ( 1 .. 3 )
{
right 120;
forward $size;
}
penup;
left 90;
forward $size;
left 90;
save;
forward $size * 1 / 4;
pendown;
box $size / 4, $size / 2;
penup;
forward $size * 3 / 8;
left 90;
forward $size / 4;
right 90;
pendown;
box $size / 4, $size / 4;
penup;
restore;
save;
forward $size / 2;
left 90;
forward $size + 40;
right 90;
pendown;
for (1 .. 8)
{
forward 15;
left 45;
forward 15;
}
restore;
penup;
}
sub graph
{
save;
my $size = shift;
my $width = $size / @_;
my $hscale = $size / max @_;
for ( @_ )
{
box $width, $hscale * $_;
save;
penup;
forward $width / 2;
left 90;
forward 10;
text $_;
pendown;
restore;
forward $width;
}
restore;
}
sub run
{
penup;
forward 50;
right 90;
forward 400;
pendown;
house(300);
penup;
forward 400;
pendown;
graph( 400, 2,7,4,5,1,8,6 );
}
Phix
You can run this online here.
I factored out some common code for the 2D and 3D versions (or started to, distributed version contains some additional diag aids)
-- demo\rosetta\turtle.e include pGUI.e global Ihandle canvas, dlg global cdCanvas cdcanvas global bool pen_down = false global procedure pendown(atom colour=CD_BLACK) pen_down = true cdCanvasSetForeground(cdcanvas, colour) end procedure global procedure penup() pen_down = false end procedure
-- -- demo\rosetta\Simple_turtle_graphics.exw -- ======================================= -- with javascript_semantics include turtle.e -- (common code for 2D and 3D versions) atom x = 0, y = 0, direction = 0 procedure walk(atom distance) // // Move forward by distance pixels. // atom start_x = x, start_y = y, angle = direction*PI/180 x += distance*sin(angle) y += distance*cos(angle) if pen_down then cdCanvasLine(cdcanvas,start_x,start_y,x,y) end if end procedure procedure right(atom angle) direction = remainder(direction+angle,360) end procedure procedure left(atom angle) right(360-angle) end procedure procedure move(sequence s) -- s is a list of angles (odd elements) -- and distances (even elements) for i=1 to length(s) do if odd(i) then right(s[i]) else walk(s[i]) end if end for end procedure procedure rectangle(atom width, height) move({0,height,90,width,90,height,90,width,90}) end procedure procedure draw_house(atom width, height) // // Draw a house at the current x,y // direction must be 0 for house to be upright -- left(10) -- (deliberately wonky works fine too) // -- (as long as you undo it at the end) // house walls rectangle(width, height) // door (maybe some windows too would be nice...) penup() move({90,width/7,-90}) pendown(CD_BLUE) rectangle(width/8,height/2.5) penup() move({-90,width/7,90}) // roof walk(height) pendown(CD_RED) atom a = arctan(width/height)*CD_RAD2DEG, d = sqrt(width*width+height*height)/2 move({a,d,180-a*2,d}) penup() // return to origin({qw,qh}) and direction 0: move({90+a,width,-90,height,180}) -- right(10) end procedure procedure draw_barchart(sequence nums, atom w, h) // draw a barchart occupying the middle 60% of w,h // nums can contain +ve and/or -ve values. -- right(10) -- (ditto) integer n = length(nums) atom mx = max(max(nums),0), mn = min(min(nums),0), r = mx-mn, -- range zl = abs(mn)/r*h*0.6+h/5, -- zero line bw = w*0.6/n -- bar width move({90,w/5,-90,zl}) pendown() for i=1 to n do atom ni = nums[i]/r*h*0.6 if ni>0 then rectangle(bw,ni) else pendown(CD_RED) right(90) rectangle(-ni,bw) left(90) pendown(CD_BLACK) end if move({90,bw,-90}) end for penup() // return to origin({w/2,0}) and direction 0: move({180,zl,90,w/5+bw*n,90}) -- left(10) end procedure function redraw_cb(Ihandle /*ih*/) integer {width, height} = IupGetIntInt(canvas, "DRAWSIZE") cdCanvasActivate(cdcanvas) cdCanvasClear(cdcanvas) atom w2 = width/2, qw = width/4, qh = height/4 penup() move({0,qh,90,qw,-90}) pendown() draw_house(qw,qh) -- house in the left half penup() move({180,qh,90,qw,90}) -- return to {0,0} move({90,w2,-90}) -- barchart in the right half draw_barchart({0.5, -4/3, 2, 1.3, 0.5},width/2,height) move({-90,w2,90}) -- return to {0,0} -- sanity checks if round(x)!=0 then ?9/0 end if if round(y)!=0 then ?9/0 end if if round(direction)!=0 then ?9/0 end if cdCanvasFlush(cdcanvas) return IUP_DEFAULT end function IupOpen() canvas = IupCanvas(Icallback("redraw_cb"),"RASTERSIZE=600x400") dlg = IupDialog(canvas,`TITLE="Simple turtle graphics"`) IupMap(dlg) cdcanvas = cdCreateCanvas(CD_IUP, canvas) IupShow(dlg) IupSetAttribute(canvas, "RASTERSIZE", NULL) -- release the minimum limitation if platform()!=JS then IupMainLoop() IupClose() end if
Python
from turtle import *
def rectangle(width, height):
for _ in range(2):
forward(height)
left(90)
forward(width)
left(90)
def square(size):
rectangle(size, size)
def triangle(size):
for _ in range(3):
forward(size)
right(120)
def house(size):
right(180)
square(size)
triangle(size)
right(180)
def barchart(lst, size):
scale = size/max(lst)
width = size/len(lst)
for i in lst:
rectangle(i*scale, width)
penup()
forward(width)
pendown()
penup()
back(size)
pendown()
clearscreen()
hideturtle()
house(150)
penup()
forward(10)
pendown()
barchart([0.5, (1/3), 2, 1.3, 0.5], 200)
penup()
back(10)
pendown()
- Output:
Quackery
[ $ "turtleduck.qky" loadfile ] now!
[ behead do
rot witheach
[ do 2over 2over
v< if 2swap
2drop ] ] is largest ( [ --> n/d )
[ 2 times
[ 2dup walk
-1 4 turn
2over walk
-1 4 turn ]
2drop 2drop ] is rectangle ( n/d n/d --> )
[ 2dup rectangle ] is square ( n/d --> )
[ 3 times
[ 2dup walk
1 3 turn ]
2drop ] is triangle ( n/d --> )
[ 1 2 turn
2dup square triangle
1 2 turn ] is house ( n/d --> )
[ stack ] is bar.width ( --> s )
[ stack ] is bar.scale ( --> s )
[ join temp put
dup size n->v
temp share do v/ 1/v
join bar.width put
dup largest
temp share do v/
join bar.scale put
witheach
[ do
bar.scale share do v/
bar.width share do
rectangle
bar.width share do fly ]
temp take do -v fly
bar.width release
bar.scale release ] is barchart ( [ n/d --> )
turtle
150 1 house
10 1 fly
' [ [ 1 2 ] [ 1 3 ] [ 2 1 ] [ 13 10 ] [ 1 2 ] ] 200 1 barchart
-10 1 fly
- Output:
Wren
import "dome" for Window
import "graphics" for Canvas, Color
import "./turtle" for Turtle
class Main {
construct new(width, height) {
Window.resize(width, height)
Canvas.resize(width, height)
Window.title = "Simple turtle graphics"
_w = width
_h = height
}
init() {
Canvas.cls(Color.white)
_t = Turtle.new()
drawHouse(_w/4)
barChart([15, 10, 50, 35, 20], _w/3)
}
drawHouse(size) {
// save initial turtle position and direction
var saveX = _t.x
var saveY = _t.y
var saveD = _t.dir
_t.pen.width = 2
// draw house
_t.drawRect(_w/4, _h/2, size, size)
// draw roof
_t.right(30)
_t.walk(size)
_t.right(120)
_t.walk(size)
// draw door
var doorWidth = (size/4).floor
var doorHeight = (size/2).floor
_t.drawRect(_w/4 + doorWidth/2, _h/2 + doorHeight, doorWidth, doorHeight)
// draw window
var windWidth = (size/3).floor
var windHeight = (size/4).floor
_t.drawRect(_w/4 + size/2, _h/2 + size/2, windWidth, windHeight)
// restore initial turtle position and direction
_t.x = saveX
_t.y = saveY
_t.dir = saveD
}
// nums assumed to be all non-negative
barChart(nums, size) {
// save intial turtle position and direction
var saveX = _t.x
var saveY = _t.y
var saveD = _t.dir
// find maximum
var max = 0
for (n in nums) if (n > max) max = n
// scale to fit within a square with sides 'size' and draw chart
var barWidth = (size / nums.count).floor
var startX = _w / 2 + 20
var startY = _h / 2
for (i in 0...nums.count) {
var barHeight = (nums[i] * size / max).round
_t.drawRect(startX, startY - barHeight, barWidth, barHeight)
startX = startX + barWidth
}
// restore intial turtle position and direction
_t.x = saveX
_t.y = saveY
_t.dir = saveD
}
update() {}
draw(alpha) {}
}
var Game = Main.new(600, 600)
- Output:
Similar to Quackery image except that the house has a door and a single window.
XPL0
def ScrW=320, ScrH=200;
def Pi = 3.141592654;
def D2R = Pi/180.0;
real Dir, PosX, PosY;
int Pen;
def \Pen\ Up, Down;
proc Turn(Ang);
real Ang;
Dir:= Dir + Ang*D2R;
proc MoveTo(Dist);
real Dist;
[PosX:= PosX + Dist*Cos(Dir);
PosY:= PosY + Dist*Sin(Dir);
if Pen = Down then
Line(ScrW/2+fix(PosX), ScrH/2-fix(PosY), $0F \white\)
else
Move(ScrW/2+fix(PosX), ScrH/2-fix(PosY));
];
proc Rectangle(Width, Height);
real Width, Height;
int N;
[for N:= 1 to 2 do
[MoveTo(Width);
Turn(90.0);
MoveTo(Height);
Turn(90.0);
];
];
proc BarGraph(List, Len, Size);
real List; int Len; real Size;
int N;
def BarWidth = 0.4;
[for N:= 0 to Len-1 do
[Rectangle(Size*BarWidth, List(N)*Size);
MoveTo(Size*BarWidth);
];
MoveTo(-Size*BarWidth*float(Len));
];
proc Triangle(Size);
real Size;
int N;
[for N:= 1 to 3 do
[MoveTo(Size);
Turn(-120.0);
];
];
proc Square(Size);
real Size;
Rectangle(Size, Size);
proc House(Size);
real Size;
[Turn(180.0);
Square(Size);
Triangle(Size);
Turn(180.0);
];
[SetVid($13); \set VGA graphics
Move(ScrW/2, ScrH/2); \start Line at center
Dir:= 0.0; PosX:= 0.0; PosY:= 0.0; Pen:= Down;
House(80.0);
Pen:= Up; MoveTo(10.0); Pen:= Down;
BarGraph([0.5, 1.0/3.0, 2.0, 1.3, 0.5], 5, 45.0);
]
Yabasic
// Rosetta Code problem: http://rosettacode.org/wiki/Simple_turtle_graphics
// Adapted from Python to Yabasic by Galileo, 01/2022
import turtle
sub rectang(width, height)
local i
for i = 1 to 2
move(height)
turn(-90)
move(width)
turn(-90)
next
end sub
sub square(size)
rectang(size, size)
end sub
sub triang(size)
local i
for i = 1 to 3
move(size)
turn(120)
next
end sub
sub house(size)
turn(180)
square(size)
triang(size)
turn(180)
end sub
sub barchart(lst$, size)
local t$(1), t(1), n, m, i, scale, width
n = token(lst$, t$())
redim t(n)
for i = 1 to n
t(i) = val(t$(i))
if t(i) > m m = t(i)
next
scale = size/m
width = size/n
for i = 1 to n
rectang(t(i)*scale, width)
pen(false)
move(width)
pen(true)
next
pen(false)
move(-size)
pen(true)
end sub
startTurtle()
color 255, 255, 255
turn(90)
house(150)
pen(false)
move(10)
pen(true)
barchart("0.5 0.333 2 1.3 0.5", 200)