LOGO turtles in ANAR+

In order to introduce computation to non-programmers, the ANAR+ library implements a version of the LOGO language with its well-known turtles.

LOGO language

With the initial aim to let kids discover geometry and logic in a playful way, the LOGO language was developed among others by Seymour Papert, a mathematician and developmental psychologist, former student of Piaget. It consisted on a 2D graphic world were turtles can be controlled through simple instructions. They can themselves trace a line along their path enabling kids to draw through instructions. The use of such virtual turtles allowed for immediate visual feedback and debugging

wikipedia

One of the important feature of the language is the notion of intrinsic geometry: instructions to the turtle are meant in a local frame of reference. Another is its procedural nature, which is however not implemented in the ANAR+ library.

ANAR+ LOGO implementation

The implementation is meant to be faithful to the original LOGO. However, since the library has a 3D world, some additional commands are needed to control the turtle in the extra dimension.

The simplest way to use a Turtle from the ANAR+ library is shown in the code below:

import processing.opengl.*;
import anar.*;
 
Turtle t = new Turtle();
 
void setup(){
  size(800,400,OPENGL);
  Anar.init(this);
  Anar.drawAxis(true);
}
 
void draw(){
  background(200);
  t.draw();
}

now that you have a turtle, issue some instructions to it, stepping into the 3rd dimension

import processing.opengl.*;
import anar.*;
 
Turtle t = new Turtle();
 
void setup(){
  size(800,400,OPENGL);
  Anar.init(this);
  Anar.drawAxis(true);
 
  t.FORWARD(10);
  t.LEFT(80);
  t.UP(25);
  t.FORWARD(30);
  t.LEFT(80);
  t.FORWARD(50);
  t.DOWN(50);
  t.LEFT(80);
  t.FORWARD(70);
 
  // equivalent in a single line
  //t.LOGO("fd 10 lt 80 up 25 fd 30 lt 80 fd 50 dn 50 lt 80 fd 70");
}
 
void draw(){
  background(200);
  t.draw();
}

repeat the same set of instructions as much as you like

import processing.opengl.*;
import anar.*;
 
Turtle t = new Turtle();
 
void setup(){
  size(800,400,OPENGL);
  Anar.init(this);
  Anar.drawAxis(true);
 
  t.REPEAT( 10, "fd 10 lt 80 up 25 fd 30 lt 80 fd 50 dn 50 lt 80 fd 70");
 
  // equivalent 
  //t.LOGO("repeat 10 [fd 10 lt 80 up 25 fd 30 lt 80 fd 50 dn 50 lt 80 fd 70]");
 
}
 
void draw(){
  background(200);
  t.draw();
}
void keyPressed(){save("ggg.jpg"); print("saved");}

Implemented LOGO instructions

Below are presented the most useful LOGO commands implemented. Refer to the JavaDoc Turtle page for additional functionality

command abbreviated description
FORWARD n FD n go forward n units
BACK n BK n go backwards n units
RIGHT n RT n turn right by n degree
LEFT n LT n turn left by n degree
UP n UP n turn up by n degree
DOWN n DW n turn down by n degree
PENUP PU stop drawing
PENDOWN PD start drawing
CLEARSCREEN CS clear screen
SETPENCOLOR r g b SETPC r g b change pen color
REPEAT n [ … ] not abbreviated repeat n times instructions in brackets
HOME HM reset the turtle at the origin


LOGO & JAVA

It is possible to mix LOGO and JAVA in the same sketch. For instance for () is the equivalent of REPEAT in JAVA.

import processing.opengl.*;
import anar.*;
 
Turtle t = new Turtle();
 
void setup(){
  size(800,400,OPENGL);
  Anar.init(this);
  Anar.drawAxis(true);
 
  for(int i=0; i<250; i++)
  {
    t.RT(1);
    t.UP(1);
    t.FORWARD(1);
  }
 
  // LOGO equivalent using repeat
  //t.LOGO("repeat 250 [rt 1 up 1 fd 1]");
 
}
 
void draw(){
  background(200);
  t.draw();
}
 
// this saves a frame when pressing any key
void keyPressed(){
  save("frame.jpg"); 
  print("saved");
}

The advantage of using JAVA is the possibility to use the number of iteration into a LOGO instruction (note the 'i' in t.FORWARD(i))

import processing.opengl.*;
import anar.*;
 
Turtle t = new Turtle();
 
void setup(){
  size(800,400,OPENGL);
  Anar.init(this);
  Anar.drawAxis(true);
 
  for(int i=0; i<250; i++)
  {
    t.RT(1);
    t.UP(1);
    t.FORWARD(i);
 
    // equivalent
    //t.LOGO("rt 1 up 1 fd "+i);
  }
 
}
 
void draw(){
  background(200);
  t.draw();
}

It is also possible to test for an iteration value using if ().

import processing.opengl.*;
import anar.*;
 
Turtle t = new Turtle();
 
void setup(){
  size(800,400,OPENGL);
  Anar.init(this);
  Anar.drawAxis(true);
 
  for(int i=0; i<250; i++)
  {
    t.RT(1);
    if (i==100) t.UP(90);
    t.FORWARD(1);
  }
 
}
 
void draw(){
  background(200);
  t.draw();
}

For the use of randomness it is possible to make use of processing built-in functions.

import processing.opengl.*;
import anar.*;
 
Turtle t = new Turtle();
 
void setup(){
  size(800,400,OPENGL);
  Anar.init(this);
  Anar.drawAxis(true);
 
  for(int i=0; i<250; i++)
  {
    t.RT(random(45));
    t.UP(1);
    t.FORWARD(i);
 
    // equivalent
    //t.LOGO("rt "+random(45)+" up 1 fd "+i);
  }
 
}
 
void draw(){
  background(200);
  t.draw();
}

If more complex arithmetics is needed, it is possible to use JAVA variables

import processing.opengl.*;
import anar.*;
 
Turtle t = new Turtle();
 
void setup(){
  size(800,400,OPENGL);
  Anar.init(this); 
  Anar.drawAxis();
 
  for(int i=0; i<1000; i++)
  {
    float a = 10*sin(i/10.0);
 
    t.RT(a);
    t.UP(1);
    t.FORWARD(1);
 
    // equivalent
    //t.LOGO("rt "+a+" up 1 fd 1");
  }
 
  // the variable a is no longer available in this part of the code
  // it only exists within the block of the for loop
  // this is related to the notion of variable *scope*
 
  Anar.camTarget(t.trace);
}
 
void draw(){
  background(255,155,155);
  t.draw();
}

The code can be simplified into blocks encapsulated in JAVA functions.

import processing.opengl.*;
import anar.*;
 
Turtle t = new Turtle();
 
void setup(){
  size(800,400,OPENGL);
  Anar.init(this); 
  Anar.drawAxis();
 
  for(int i=0; i<10; i++)
  {
    DOTTED(10);
    t.RT(36);
  }
 
  Anar.camTarget(t.trace);
}
 
 
// a dotted line using penUp and penDown serval times
// example logo instruction  REPEAT 10 [ FD 1 PU FD 1 PD ]
void DOTTED(int L){
  t.LOGO("REPEAT "+L+" [ FD 1 PU FD 1 PD ]");
}
 
void draw(){
  background(255,155,155);
  t.draw();
}

Alternatively it is possible to write LOGO instructions in a separate file and read it from processing

import processing.opengl.*;
import anar.*;
/*
 * Example for Anar library by Guillaume LaBelle + Julien Nembrini
 * http://anar.ch
 */
Turtle t = new Turtle();
void setup(){
  size(800,400,OPENGL);
  Anar.init(this);
  Anar.drawAxis(true);
  String[] stuff = loadStrings("my.logo");
  println(stuff);
  for(int i=0; i<stuff.length; i++)
    t.LOGO(stuff[i]+" ");
}
void draw(){
  background(200);
  t.draw();
}

with the the following file my.logo located in <sketch folder>/data

forward 100
lt 45
forward 100
repeat 100 [ forward  5 rt 5 up 6  forward 3  lt 5      forward 2  ]

Multiple turtles

Nothing prevents from having several turtles at the same time. Below an example using the parametric capabilities of ANAR+:

http://anar.ch/news/bot/example/518/04zturtlemulti/

Super Turtle (extending the turtle)

JAVA uses the concept of object. One example is the Turtle object. This object can be made to have more capabilities by extending it. The code below show how to add a DOTTED line capability to the turtles.

import processing.opengl.*;
import anar.*;
/*
 * Example for Anar library by Guillaume LaBelle + Julien Nembrini
 * http://anar.ch
 */
SuperTurtle t = new SuperTurtle();
 
class SuperTurtle extends Turtle {
  // this is needed for Java
  SuperTurtle() {
    super();
  }
 
  // this is a function that will draw a dotted line of length l 
  // with dots of length dl
  void DOTTEDLINE (float l, float dl) {
    // transform from float to int
    int ll = int(l);
    int dd = int(dl);
    // compute the number of dots/undots
    int nDots = ll/dd;
    // start pen down
    boolean drawing = true;
    for (int i=0; i<nDots; i++) {
      if (drawing) 
        LOGO("pd fd "+dl);
      else 
        LOGO("pu fd "+dl);
      // alternate drawing and not drawing
      drawing = !drawing;  
    }
    // draw the remaining length
    // works also if the dot is longer than the length
    if (ll%dd > 0) LOGO("pd fw "+ll%dd);
    // make sure at the end that the pen is up 
    // for next move
    LOGO("pu");
  } 
}
 
void setup(){
  size(800,400,OPENGL);
  Anar.init(this);
  Anar.drawAxis(true);
  for(int i=0; i<200; i++)
  {
    float a = 100;
    t.RT(random(10));
    t.DOTTEDLINE(a,9);
    for (int j=0; j<200; j++)
    {
      float b = 5;
      t.LOGO("up "+random(3)+" lt "+random(4));
      b++;
    }
  }
 
  Anar.camTarget(t.trace);
}
 
void draw(){
  background(255,25,25);
  t.draw();
}