/* TURTLE GRAPHICS
* Direct | JavaScript | Description
* --------------+------------------+----------------------------
* FD n | FD(n) | Move pen n units forward
* BK n | BK(n) | Move pen n units back
* LT deg | LT(deg) | Turn pen deg degrees left
* RT deg | RT(deg) | Turn pen deg degrees right
* UP | UP() | Pen up - move only
* DN | DN() | Pen down - draw
* WIDTH w | WIDTH(w) | Set stroke width
* COLOR c | COLOR("c") | Set drawing color
*/
/* Draws a colored square */
COLOR("red") FD(10) LT(90)
COLOR("yellow") FD(10) LT(90)
COLOR("green") FD(10) LT(90)
COLOR("blue") FD(10) LT(90)
// You can loop over a section of code like so:
REPEAT 12 BEGIN
FD(10)
LT(30)
END
// REPEAT loops can be nested:
REPEAT 6 BEGIN
WIDTH(2) FD(10) LT(60)
WIDTH(1) RT(45)
REPEAT 12 BEGIN
FD(2)
RT(30)
END
LT(45)
END
// Using constants makes the code easier to read and change:
const nr_segments = 12
REPEAT nr_segments BEGIN
FD(10)
LT(360/nr_segments)
END
/* Functions group instructions, that can be called from different
* locations in the program.
*/
function nested_polygon( outer_steps, inner_steps ) {
const outer_size = 10
const outer_width = 2
const outer_angle = 360/outer_steps
const inner_size = 5
const inner_width = 1
const inner_angle = 360/inner_steps
const inner_rotation = (outer_angle + inner_angle)/2
REPEAT outer_steps BEGIN
WIDTH(outer_width)
FD(outer_size)
LT(outer_angle)
RT(inner_rotation)
WIDTH(inner_width)
REPEAT inner_steps BEGIN
FD(inner_size)
RT(inner_angle)
END
LT(inner_rotation)
END
}
nested_polygon(4, 3)
nested_polygon(4, 5)
// Unlike constants, variables can change their contents in run time.
const colors = [ // Define an array of strings
"red", "yellow", "green",
"cyan", "blue", "magenta",
];
var index = 0; // Counts through the colors
WIDTH(5); UP(); RT(90); FD(11.5); LT(90); // Move down 11.5 units,
BK(2) // centering the circle
REPEAT 12 BEGIN
index = index + 1 // Select next color
// The colors[] array has a range of [0..5] in this example.
// So we clamp index to values from 0 to (colors.length-1).
// "%" is like "/", but the result is the remainder of the division:
index = index % colors.length
COLOR( colors[index] ) // Set the color
DN() FD(4) // Draw line
UP() FD(1) LT(30) FD(1) // Move to next line
END
// "The code is the documentation."
// - 1337 hax0r
function recurse( distance, angle ) {
if (distance > 0) {
FD(distance);
LT(angle);
recurse(distance - 1, angle);
}
}
function multiple( distance, angle, iterations ) {
for( let i = 0 ; i < iterations ; ++i ) {
recurse(distance, angle);
}
}
const selected_variant = 0;
switch (selected_variant) {
case 1: recurse ( 20, 30 ); break;
case 2: multiple( 20, 30, 3 ); break;
case 3: recurse ( 10, 60 ); break;
case 4: multiple( 10, 60, 3 ); break;
default: throw Error("Oopsie...");
}
const angle_reduction = 0.8
const length_reduction = 0.65
const hex = "0123456789abcdef";
function draw_branch( angle = 50, length = 32 ) {
if (length < 1) return;
const brightness = Math.floor((32 - length)/5);
COLOR("#6" + hex.charAt(brightness+2) + "0")
WIDTH(length/2)
FD(length)
PUSH()
LT(angle)
draw_branch(
angle *angle_reduction,
length*length_reduction
)
POP()
PUSH()
RT(angle)
draw_branch(
angle *angle_reduction,
length*length_reduction
)
POP()
}
RESET(NO_GRID) BACKGROUND("#030")
LT(90) UP() BK(40) DN()
draw_branch()
HIDE()
/* EcmaTurtle provides you with a set of functions, like FD, REPEAT, etc.
* Let's take a look, how REPEAT is implemented:
*/
function MyRepeat( times, callback ) {
while (times > 0) { await
callback(); // Please ignore the "await".
times = times - 1;
}
}
/* The function takes another function as parameter (callback) and
* executes that function repeatedly. It is used like so:
*/
const do_something = async function() {
// My parser can't handle callbacks yet
//...Xunction do_something() {
FD(10)
LT(30)
}
MyRepeat(12, do_something);
/* You don't have to declare a function for using it with repeat;
* instead, use "anomnymous functions", which are declared on the spot:
*/
REPEAT(
12,
async function() { // Ignore the "async" for now
FD(10)
LT(30)
}
);
// Arrow functions can be used in similar fashion to anonymous functions:
REPEAT( 12, ()=>{
FD(10)
LT(30)
});
/* In reality, all the code you enter here is JavaScript. Before execution,
* parts of your code will be replaced with real JS code, for example:
*
* REPEAT 12 BEGIN
* FD(10) LT(30)
* END
*
* will be turned into the following code:
*/
// REPEAT --> "REPEAT("
REPEAT(12, ()=>{ // BEGIN --> ", ()=>{"
FD(10); LT(30);
}); // END --> "});"
/* The take-away of this lesson is, that EcmaTurtle's replacements may do
* something silly, causing your program to crash. Of course I did some
* testing and most things will work just fine. If your program crashes
* for no apparent reason and you suspect EcmaTurtle to be the culprit,
* you can check the actual code being run; It is dumped to the Developer
* Console of your browser before execution.
*/
//...SPEED(NO_ANIMATION)
const max_segments = 15 // 16 hexadecimal digits
const rotation_speed = 5;
BACKGROUND("black")
function draw_circles( nr_segments, rotation ) {
RESET(NO_GRID | NO_TURTLE | FULL_SPEED)
LT(rotation)
const hex_digits = "0123456789abcdef";
const R = hex_digits.charAt(nr_segments * (rotation % 3 == 0));
const G = hex_digits.charAt(nr_segments * (rotation % 3 == 1));
const B = hex_digits.charAt(nr_segments * (rotation % 3 == 2));
COLOR("#"+R+G+B);
for( let r=0 ; r<nr_segments ; ++r ) {
for( let i=0 ; i<36 ; ++i ) {
FD(2)
LT(10)
}
RT(360/nr_segments)
}
RT(360/max_segments)
SLEEP(33)
UPDATE()
}
let r = 0
while (true) {
for( let s=2 ; s<max_segments ; ++s ) {
draw_circles(s, r)
r += rotation_speed
}
for( let s=max_segments ; s>2 ; --s ) {
draw_circles(s, r)
r += rotation_speed
}
}
const nr_segments = 36
const radius = 50
function circle( radius, nr_segments ) {
const circumference = 2*radius*π
const segment_length = circumference/nr_segments
UP()
FD(radius) LT(90) BK(segment_length/2)
DN()
REPEAT nr_segments BEGIN
FD(segment_length)
LT(360/nr_segments)
END
UP()
FD(segment_length/2) RT(90) BK(radius)
DN()
}
function coordinates( radius ) {
const length = 2*(radius+10)
const dash_length = 2;
const nr_dashes = length/(2*dash_length)+1
const distance = length/2 + dash_length/2
LT(90)
UP() BK(distance) DN()
REPEAT nr_dashes BEGIN FD(2) UP() FD(2) DN() END
UP() BK(distance+dash_length) DN()
RT(90)
UP() BK(distance) DN()
REPEAT nr_dashes BEGIN FD(2) UP() FD(2) DN() END
UP() BK(distance+dash_length) DN()
}
function triangle(radius, angle) {
const x = COS(angle)*radius
const y = SIN(angle)*radius
FD(x) LT(90)
FD(y) LT(90+angle)
FD(radius)
LT(180-angle)
}
WIDTH(1) COLOR("black") coordinates(radius)
WIDTH(4) COLOR("black") circle(radius, nr_segments)
WIDTH(2) COLOR("red") triangle(radius, 30)
WIDTH(2) COLOR("green") triangle(radius, -45)
WIDTH(2) COLOR("blue") triangle(radius, 120)
const letter_width = 9;
const letter_gap = 4;
const curvature = 5;
const text = "Hello, World!";
function draw_character( character ) {
const w = letter_width // Typical letter width
switch (character.toUpperCase()) {
case "H":
LT(90) FD(20)
UP() BK(10) DN()
RT(90) FD(w) LT(90)
UP() FD(10) DN()
BK(20) RT(90)
break
case "E":
LT(90) FD(20) RT(90) FD(w)
UP() RT(90) FD(10) RT(90) FD(3) DN()
FD(w-3)
UP() LT(90) FD(10) LT(90) DN()
FD(w)
break
case "L":
LT(90) FD(20)
UP() BK(20) DN()
RT(90) FD(w)
break
case "O":
UP() LT(90) FD(10) DN()
REPEAT 3 BEGIN FD(4) RT(30) END
RT(30)
REPEAT 3 BEGIN FD(4) RT(30) END
LT(30)
REPEAT 3 BEGIN FD(4) RT(30) END
RT(30)
REPEAT 3 BEGIN FD(4) RT(30) END
LT(30)
UP() BK(10) RT(90) FD(11) DN()
break
case ",":
LT(75) BK(5) FD(5)
REPEAT 6 BEGIN FD(1) LT(60) END
RT(75)
break
//case "W":
//break
//case "R":
//break
//case "D":
//break
case "!":
LT(90) FD(1)
UP() FD(2) DN()
FD(17)
UP() BK(20) DN()
RT(90)
break
case " ":
UP() FD(8) DN()
break
default:
COLOR("red") FD(w) COLOR("BLACK")
} // switch
UP() FD(letter_gap) DN()
}
function draw_text( text, angle=0 ) {
// Move half the text width to the left
UP()
REPEAT text.length BEGIN BK((letter_width+letter_gap)/2) END
FD( text.length * ((360 - curvature) / 360) )
DN()
// Turn left half as much, as the loop below will turn right in total
const length
= text.length
- 1 // The "," has virtually no width
- 1 // The "!" has virtually no width
;
LT( length*angle/2 )
// Draw each character
for( let i=0 ; i<text.length ; ++i ) {
const character = text.charAt(i);
draw_character(character);
RT(angle) // Turn right after each char, creating an arc
}
}
WIDTH(3)
draw_text(text, curvature)
HIDE()
// Bannkreis
GRID(OFF)
BACKGROUND("black")
const radius = 50
const nr_segments = 360/5
const segment_length = 2*π*radius/nr_segments
COLOR("#642") WIDTH(22)
LT(72*3)
UP() FD(radius) LT(90) BK(segment_length/2) DN()
REPEAT nr_segments BEGIN
FD(segment_length)
LT(360/nr_segments)
END
UP() FD(segment_length/2) RT(90) BK(radius) DN()
LT(72*3)
UP() LT(90) FD(16.2) RT(90) BK(50) DN()
COLOR("black") WIDTH(33)
REPEAT 5 BEGIN
FD(38);
UP(); FD(2); DN()
FD(60)
RT(180*4/5)
END
COLOR("#ca0") WIDTH(1.5)
REPEAT 5 BEGIN
FD(38);
UP(); FD(3.25); DN()
FD(58.25)
RT(180*4/5)
END
RT(180*1/5/2)
UP() FD(10) DN()
LT(180*1/5/2)
REPEAT 5 BEGIN
FD(27.5);
UP(); FD(3.25); DN()
FD(49.75)
RT(180 *4/5)
END
HIDE()
// New Program