Skip to content

Commit

Permalink
Add SETTURTLE to change between multiple turtles (#143)
Browse files Browse the repository at this point in the history
Also, add TURTLE/TURTLES to get the current/max turtle index.
Also, add CLEARTURTLES to clear all the turtles.
Also, add ASK <n> [ .. ] for quickly switching to another turtle.

All inspired by the respective commands in FMSLogo.
  • Loading branch information
bojidar-bg authored Sep 20, 2023
1 parent 25117e0 commit cce1740
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 41 deletions.
8 changes: 8 additions & 0 deletions examples.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,11 @@ to fern :size :sign
end
window clearscreen pu bk 150 pd
fern 25 1

clearscreen
setturtle 2 penup right 90 forward 100 left 90 pendown
repeat 100 [
setturtle 1 forward random 4
setturtle 2 forward random 4
wait 2
]
19 changes: 19 additions & 0 deletions language.html
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,19 @@ <h4>6.3 Turtle and Window Control</h4>
<dt><code>setscrunch <var>sx</var> <var>sy</var></code>
<dd>Set the graphics scaling factors
<dd class=example>setscrunch 1 2 arc 360 100</dd>

<dt><code>setturtle <var>index</var></code>
<dd>Switch to the turtle numbered <var>index</var> (starting from 1 for the default turtle present at start).
If the turtle has not been used yet, it will be created at the center, facing upwards, visible, with the pen down.
<dd class=example>setturtle 2 rt 90 fd 100</dd>

<dt><code>ask <var>turtleindex</var> [ <var>statements ...</var> ]</code>
<dd>Execute <var>statements</var> as turtle number <var>turtleindex</var>.
<dd class=example>ask 2 [ rt 90 fd 100 ]</dd>

<dt><code>clearturtles</code>
<dd>Remove all turtles, keeping the current one as index 1.
<dd class=example>fd 50 setturtle 2 rt 90 fd 100 clearturtles </dd>
</dl>

<h4>6.4 Turtle and Window Queries</h4>
Expand All @@ -697,6 +710,12 @@ <h4>6.4 Turtle and Window Queries</h4>

<dt><code>labelfont</code>
<dd>Outputs the name of the font drawn by <code>label</code>

<dt><code>turtle</code>
<dd>Outputs the index of the currently-active turtle

<dt><code>turtles</code>
<dd>Outputs the largest index that has been passed to <code>setturtle</code>
</dl>

<h4>6.5 Pen and Background Control</h4>
Expand Down
33 changes: 33 additions & 0 deletions logo.js
Original file line number Diff line number Diff line change
Expand Up @@ -2243,6 +2243,31 @@ function LogoInterpreter(turtle, stream, savehook)
// Not Supported: refresh
// Not Supported: norefresh

def("setturtle", function(index) {
index = aexpr(index)|0;
if (index < 1)
throw err("{_PROC_}: Expected positive turtle index", ERRORS.BAD_INPUT);
turtle.currentturtle = index - 1;
});

def("ask", function(index, statements) {
index = aexpr(index)|0;
if (index < 1)
throw err("{_PROC_}: Expected positive turtle index", ERRORS.BAD_INPUT);
statements = reparse(lexpr(statements));
var originalturtle = turtle.currentturtle;
turtle.currentturtle = index - 1;
return promiseFinally(
this.execute(statements),
function() {
turtle.currentturtle = originalturtle;
});
});

def("clearturtles", function() {
turtle.clearturtles();
});

//
// 6.4 Turtle and Window Queries
//
Expand All @@ -2265,6 +2290,14 @@ function LogoInterpreter(turtle, stream, savehook)
return turtle.fontname;
});

def("turtles", function() {
return turtle.turtles;
});

def("turtle", function() {
return turtle.currentturtle + 1;
});

//
// 6.5 Pen and Background Control
//
Expand Down
22 changes: 21 additions & 1 deletion tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -989,7 +989,7 @@ QUnit.test("Logical Operations", function(t) {
});

QUnit.test("Graphics", function(t) {
t.expect(166);
t.expect(180);

// NOTE: test canvas is 300,300 (so -150...150 coordinates before hitting)
// edge
Expand Down Expand Up @@ -1157,6 +1157,17 @@ QUnit.test("Graphics", function(t) {

this.run('cs setscrunch 1 1');

this.assert_equals('cs fd 100 setturtle 2 pos', [0, 0]);
this.assert_equals('cs fd 100 setturtle 2 rt 90 setturtle 1 pos', [0, 100]);
this.assert_equals('cs ht setturtle 2 shownp', 1);
this.assert_equals('cs setturtle 2 ht setturtle 1 shownp', 1);
this.assert_equals('cs ht setturtle 2 setturtle 1 shownp', 0);
this.assert_equals('cs ht ask 2 [ pu ] shownp', 0);
this.assert_equals('cs pu setturtle 2 pendownp', 1);
this.assert_equals('cs setturtle 2 pu setturtle 1 pendownp', 1);
this.assert_equals('cs pu setturtle 2 setturtle 1 pendownp', 0);
this.assert_equals('cs pu ask 2 [ ht ] pendownp', 0);

//
// 6.4 Turtle and Window Queries
//
Expand All @@ -1177,6 +1188,10 @@ QUnit.test("Graphics", function(t) {
this.assert_equals('make "x ( item 1 bounds ) setscrunch 2 1 :x = (item 1 bounds) * 2', 1);
this.assert_equals('make "y ( item 3 bounds ) setscrunch 1 3 :x = (item 3 bounds) * 3', 1);

this.assert_equals('clearturtles turtle', 1);
this.assert_equals('clearturtles setturtle 10 setturtle 5 turtle', 5);
this.assert_equals('clearturtles setturtle 10 setturtle 5 turtles', 10);
this.assert_equals('clearscreen setturtle 3 turtles', 3);

//
// 6.5 Pen and Background Control
Expand Down Expand Up @@ -1996,6 +2011,7 @@ QUnit.test("Arity of Primitives", function(t) {
['arraytolist', [1, 1, 1]],
['ascii', [1, 1, 1]],
['ashift', [2, 2, 2]],
['ask', [2, 2, 2]],
['back', [1, 1, 1]],
['background', [0, 0, 0]],
['before?', [2, 2, 2]],
Expand Down Expand Up @@ -2025,6 +2041,7 @@ QUnit.test("Arity of Primitives", function(t) {
['clean', [0, 0, 0]],
['clearscreen', [0, 0, 0]],
['cleartext', [0, 0, 0]],
['clearturtles', [0, 0, 0]],
['clickpos', [0, 0, 0]],
//['close', [1, 1, 1]],
//['co', [0, 1, 1]],
Expand Down Expand Up @@ -2254,6 +2271,7 @@ QUnit.test("Arity of Primitives", function(t) {
//['settemploc', [1, 1, 1]],
['settextcolor', /*[2, 2, 2]*/ [1, 1, 1]], /* Does not support background color */
['settextsize', [1, 1, 1]],
['setturtle', [1, 1, 1]],
//['setwrite', [1, 1, 1]],
//['setwritepos', [1, 1, 1]],
['setx', [1, 1, 1]],
Expand Down Expand Up @@ -2293,7 +2311,9 @@ QUnit.test("Arity of Primitives", function(t) {
//['traced?', [1, 1, 1]],
//['tracedp', [1, 1, 1]],
//['ts', [0, 0, 0]],
['turtle', [0, 0, 0]],
['turtlemode', [0, 0, 0]],
['turtles', [0, 0, 0]],
['type', [0, 1, -1]],
['unbury', [1, 1, 1]],
//['unstep', [1, 1, 1]],
Expand Down
134 changes: 94 additions & 40 deletions turtle.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@
this._buttons = 0;
this._touches = [];

this._turtles = [{}];
this._currentturtle = 0;

this._init();
this._tick();

Expand Down Expand Up @@ -122,7 +125,7 @@

requestAnimationFrame(this._tick.bind(this));
var cur = JSON.stringify([this.x, this.y, this.r, this.visible,
this.sx, this.sy, this.width, this.height]);
this.sx, this.sy, this.width, this.height, this._turtles]);
if (cur === this._last_state) return;
this._last_state = cur;

Expand All @@ -131,48 +134,60 @@
this.turtle_ctx.clearRect(0, 0, this.width, this.height);
this.turtle_ctx.restore();

if (this.visible) {
var ctx = this.turtle_ctx;
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(Math.PI/2 + this.r);
ctx.beginPath();

var points = [
[0, -20], // Head
[2.5, -17],
[3, -12],

[6, -10],
[9, -13], // Arm
[13, -12],
[18, -4],
[18, 0],
[14, -1],
[10, -7],

[8, -6], // Shell
[10, -2],
[9, 3],
[6, 10],

[9, 13], // Foot
[6, 15],
[3, 12],

[0, 13],
];

points.concat(points.slice(1, -1).reverse().map(invert))
.forEach(function(pair, index) {
ctx[index ? 'lineTo' : 'moveTo'](pair[0], pair[1]);
});
function _draw(ctx, turtle) {
if (turtle.visible) {
ctx.save();
ctx.translate(turtle.x, turtle.y);
ctx.rotate(Math.PI/2 + turtle.r);
ctx.beginPath();

var points = [
[0, -20], // Head
[2.5, -17],
[3, -12],

[6, -10],
[9, -13], // Arm
[13, -12],
[18, -4],
[18, 0],
[14, -1],
[10, -7],

[8, -6], // Shell
[10, -2],
[9, 3],
[6, 10],

[9, 13], // Foot
[6, 15],
[3, 12],

[0, 13],
];

points.concat(points.slice(1, -1).reverse().map(invert))
.forEach(function(pair, index) {
ctx[index ? 'lineTo' : 'moveTo'](pair[0], pair[1]);
});

ctx.closePath();
ctx.stroke();

ctx.restore();
}
}

ctx.closePath();
ctx.stroke();
_draw(this.turtle_ctx, this);

ctx.restore();
for (var i in this._turtles) {
if (this._turtles[i] === undefined) {
continue;
}
_draw(this.turtle_ctx, this._turtles[i]);
}


}},

_moveto: {value: function(x, y, setpos) {
Expand Down Expand Up @@ -369,6 +384,7 @@

clearscreen: {value: function() {
this.home();
this.clearturtles();
this.clear();
}},

Expand All @@ -384,6 +400,11 @@
}
}},

clearturtles: {value: function() {
this._turtles = [{}];
this._currentturtle = 0;
}},

home: {value: function() {
this._moveto(0, 0);
this.r = deg2rad(90);
Expand Down Expand Up @@ -624,6 +645,39 @@

touches: {
get: function() { return this._touches; }
},

currentturtle: {
get: function() { return this._currentturtle; },
set: function(newturtle) {
if (newturtle === this._currentturtle) return;
this._turtles[this._currentturtle] = {
x: this.x,
y: this.y,
r: this.r,
pendown: this.pendown,
visible: this.visible,
};
this._currentturtle = newturtle;
if (this._turtles[this._currentturtle] !== undefined) {
this.x = this._turtles[this._currentturtle].x;
this.y = this._turtles[this._currentturtle].y;
this.r = this._turtles[this._currentturtle].r;
this.pendown = this._turtles[this._currentturtle].pendown;
this.visible = this._turtles[this._currentturtle].visible;
} else {
this.x = 0;
this.y = 0;
this.r = Math.PI / 2;
this.pendown = true;
this.visible = true;
}
this._turtles[this._currentturtle] = {};
}
},

turtles: {
get: function() { return this._turtles.length; }
}

});
Expand Down

0 comments on commit cce1740

Please sign in to comment.