Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWalter Bender <walter@sugarlabs.org>2013-07-06 14:33:49 (GMT)
committer Walter Bender <walter@sugarlabs.org>2013-07-06 14:33:49 (GMT)
commit09da0eafffdbbe790de5c2f0c0d6dd8d944a8ff2 (patch)
tree53d01841707e2c88d5e72d3285350443643161b1
parentee9b0bb75a2a54bbdccea5b93cea2285bef7e3a2 (diff)
parentf5f60856ba3a8101b0935cc14483eec3050c08fd (diff)
Merge branch 'turtle-centric-2' into merge-workv184
-rw-r--r--NEWS5
-rw-r--r--TurtleArt/tabasics.py342
-rw-r--r--TurtleArt/tacanvas.py607
-rw-r--r--TurtleArt/tacollaboration.py86
-rw-r--r--TurtleArt/talogo.py56
-rw-r--r--TurtleArt/taturtle.py737
-rw-r--r--TurtleArt/tawindow.py289
-rw-r--r--activity/activity.info2
-rw-r--r--plugins/turtle_blocks_extras/turtle_blocks_extras.py323
-rw-r--r--pysamples/dotted_line.py73
-rw-r--r--pysamples/forward_push.py27
-rw-r--r--pysamples/load_block.py90
-rw-r--r--pysamples/uturn.py23
-rw-r--r--samples/game-continents.ta526
-rw-r--r--samples/game-find-boston.ta750
-rw-r--r--samples/game-shapes-and-color.ta783
16 files changed, 2263 insertions, 2456 deletions
diff --git a/NEWS b/NEWS
index 6389551..952e60e 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,8 @@
+183
+
+ENHANCEMENT:
+* Internal refactoring in support of a turtle-centric approach to programming
+
182
ENHANCEMENT:
diff --git a/TurtleArt/tabasics.py b/TurtleArt/tabasics.py
index 3f960da..04a74d1 100644
--- a/TurtleArt/tabasics.py
+++ b/TurtleArt/tabasics.py
@@ -19,7 +19,7 @@
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
#THE SOFTWARE.
-"""
+'''
This file contains the constants that by-in-large determine the
behavior of Turtle Art. Notably, the block palettes are defined
below. If you want to add a new block to Turtle Art, you could
@@ -43,9 +43,13 @@ add_block method in the Palette class.
# Next, you need to define what your block will do:
# def_prim takes 3 arguments: the primitive name, the number of
# arguments -- 0 in this case -- and the function to call -- in this
- # case, the canvas.seth function to set the heading.
- self.tw.lc.def_prim('uturn', 0,
- lambda self: self.tw.canvas.seth(self.tw.canvas.heading + 180))
+ # case, we define the _prim_uturn function to set heading += 180.
+ self.tw.lc.def_prim('uturn', 0, lambda self: self._prim_uturn)
+ def _prim_uturn(self):
+ value = self.tw.turtles.get_active_turtle().get_heading() + 180
+ self.tw.turtles.get_active_turtle().set_heading(value)
+ if self.tw.lc.update_values:
+ self.tw.lc.update_label_value('heading', value)
That's it. When you next run Turtle Art, you will have a 'uturn' block
on the 'mypalette' palette.
@@ -56,7 +60,7 @@ mypaletteoff.svg and mypaletteon.svg, where 'mypalette' is the same
string as the entry you used in instantiating the Palette class. Note
that the icons should be the same size (55x55) as the others. (This is
the default icon size for Sugar toolbars.)
-"""
+'''
from time import time, sleep
from math import sqrt
@@ -78,22 +82,22 @@ def _color_to_num(c):
def _num_type(x):
- """ Is x a number type? """
+ ''' Is x a number type? '''
if isinstance(x, (int, float)):
return True
return False
def _millisecond():
- """ Current time in milliseconds """
+ ''' Current time in milliseconds '''
return time() * 1000
class Palettes():
- """ a class for creating the palettes of blocks """
+ ''' a class for creating the palettes of blocks '''
- def __init__(self, parent):
- self.tw = parent
+ def __init__(self, turtle_window):
+ self.tw = turtle_window
self._turtle_palette()
@@ -113,7 +117,7 @@ class Palettes():
# Palette definitions
def _turtle_palette(self):
- """ The basic Turtle Art turtle palette """
+ ''' The basic Turtle Art turtle palette '''
palette = make_palette('turtle',
colors=["#00FF00", "#00A000"],
@@ -127,10 +131,11 @@ class Palettes():
default=100,
logo_command='forward',
help_string=_('moves turtle forward'))
- self.tw.lc.def_prim('forward', 1,
- lambda self, x:
- primitive_dictionary['move']
- (self.tw.canvas.forward, x))
+ self.tw.lc.def_prim(
+ 'forward',
+ 1,
+ lambda self, x: primitive_dictionary['move'](
+ self.tw.turtles.get_active_turtle().forward, x))
palette.add_block('back',
style='basic-style-1arg',
@@ -142,9 +147,10 @@ class Palettes():
self.tw.lc.def_prim('back', 1,
lambda self, x:
primitive_dictionary['move']
- (self.tw.canvas.forward, x, reverse=True))
+ (self.tw.turtles.get_active_turtle().forward, x,
+ reverse=True))
- primitive_dictionary['clean'] = self.tw.lc.prim_clear
+ primitive_dictionary['clean'] = self._prim_clear
palette.add_block('clean',
style='basic-style-extended-vertical',
label=_('clean'),
@@ -152,8 +158,10 @@ class Palettes():
logo_command='clean',
help_string=_('clears the screen and reset the \
turtle'))
- self.tw.lc.def_prim('clean', 0,
- lambda self: primitive_dictionary['clean']())
+ self.tw.lc.def_prim(
+ 'clean',
+ 0,
+ lambda self: primitive_dictionary['clean']())
primitive_dictionary['right'] = self._prim_right
palette.add_block('left',
@@ -176,8 +184,10 @@ in degrees)'))
logo_command='right',
help_string=_('turns turtle clockwise (angle in \
degrees)'))
- self.tw.lc.def_prim('right', 1,
- lambda self, x: primitive_dictionary['right'](x))
+ self.tw.lc.def_prim(
+ 'right',
+ 1,
+ lambda self, x: primitive_dictionary['right'](x))
primitive_dictionary['arc'] = self._prim_arc
palette.add_block('arc',
@@ -187,10 +197,11 @@ degrees)'))
default=[90, 100],
logo_command='taarc',
help_string=_('moves turtle along an arc'))
- self.tw.lc.def_prim('arc', 2,
- lambda self, x, y:
- primitive_dictionary['arc']
- (self.tw.canvas.arc, x, y))
+ self.tw.lc.def_prim(
+ 'arc',
+ 2,
+ lambda self, x, y: primitive_dictionary['arc'](
+ self.tw.turtles.get_active_turtle().arc, x, y))
define_logo_function('taarc', 'to taarc :a :r\nrepeat round :a \
[right 1 forward (0.0175 * :r)]\nend\n')
@@ -202,10 +213,11 @@ degrees)'))
default=[0, 0],
help_string=_('moves turtle to position xcor, ycor; \
(0, 0) is in the center of the screen.'))
- self.tw.lc.def_prim('setxy2', 2,
- lambda self, x, y:
- primitive_dictionary['move']
- (self.tw.canvas.setxy, x, y))
+ self.tw.lc.def_prim(
+ 'setxy2',
+ 2,
+ lambda self, x, y: primitive_dictionary['move'](
+ self.tw.turtles.get_active_turtle().set_xy, (x, y)))
define_logo_function('tasetxy', 'to tasetxy :x :y\nsetxy :x :y\nend\n')
primitive_dictionary['set'] = self._prim_set
@@ -217,10 +229,11 @@ degrees)'))
logo_command='seth',
help_string=_('sets the heading of the turtle (0 is \
towards the top of the screen.)'))
- self.tw.lc.def_prim('seth', 1,
- lambda self, x:
- primitive_dictionary['set']
- ('heading', self.tw.canvas.seth, x))
+ self.tw.lc.def_prim(
+ 'seth',
+ 1,
+ lambda self, x: primitive_dictionary['set'](
+ 'heading', self.tw.turtles.get_active_turtle().set_heading, x))
palette.add_block('xcor',
style='box-style',
@@ -231,7 +244,10 @@ the turtle (can be used in place of a number block)'),
prim_name='xcor',
logo_command='xcor')
self.tw.lc.def_prim(
- 'xcor', 0, lambda self: self.tw.canvas.xcor / self.tw.coord_scale)
+ 'xcor',
+ 0,
+ lambda self: self.tw.turtles.get_active_turtle().get_xy()[0] /
+ self.tw.coord_scale)
palette.add_block('ycor',
style='box-style',
@@ -242,7 +258,10 @@ the turtle (can be used in place of a number block)'),
prim_name='ycor',
logo_command='ycor')
self.tw.lc.def_prim(
- 'ycor', 0, lambda self: self.tw.canvas.ycor / self.tw.coord_scale)
+ 'ycor',
+ 0,
+ lambda self: self.tw.turtles.get_active_turtle().get_xy()[1] /
+ self.tw.coord_scale)
palette.add_block('heading',
style='box-style',
@@ -253,7 +272,9 @@ turtle (can be used in place of a number block)'),
prim_name='heading',
logo_command='heading')
self.tw.lc.def_prim(
- 'heading', 0, lambda self: self.tw.canvas.heading)
+ 'heading',
+ 0,
+ lambda self: self.tw.turtles.get_active_turtle().get_heading())
palette.add_block('turtle-label',
hidden=True,
@@ -270,15 +291,17 @@ turtle (can be used in place of a number block)'),
logo_command='tasetxypenup',
help_string=_('moves turtle to position xcor, ycor; \
(0, 0) is in the center of the screen.'))
- self.tw.lc.def_prim('setxy', 2,
- lambda self, x, y:
- primitive_dictionary['move']
- (self.tw.canvas.setxy, x, y, pendown=False))
+ self.tw.lc.def_prim(
+ 'setxy',
+ 2,
+ lambda self, x, y: primitive_dictionary['move'](
+ self.tw.turtles.get_active_turtle().set_xy, (x, y),
+ pendown=False))
define_logo_function('tasetxypenup', 'to tasetxypenup :x :y\npenup\n\
setxy :x :y\npendown\nend\n')
def _pen_palette(self):
- """ The basic Turtle Art pen palette """
+ ''' The basic Turtle Art pen palette '''
palette = make_palette('pen',
colors=["#00FFFF", "#00A0A0"],
@@ -293,8 +316,10 @@ setxy :x :y\npendown\nend\n')
logo_command='tasetbackground',
help_string=_('fills the background with (color, \
shade)'))
- self.tw.lc.def_prim('fillscreen', 2,
- lambda self, x, y: self.tw.canvas.fillscreen(x, y))
+ self.tw.lc.def_prim(
+ 'fillscreen',
+ 2,
+ lambda self, x, y: self.tw.canvas.fillscreen(x, y))
palette.add_block('fillscreen2',
style='basic-style-3arg',
@@ -305,9 +330,10 @@ shade)'))
logo_command='tasetbackground',
help_string=_('fills the background with (color, \
shade)'))
- self.tw.lc.def_prim('fillscreen2', 3,
- lambda self, x, y, z:
- self.tw.canvas.fillscreen_with_gray(x, y, z))
+ self.tw.lc.def_prim(
+ 'fillscreen2',
+ 3,
+ lambda self, x, y, z: self.tw.canvas.fillscreen_with_gray(x, y, z))
define_logo_function('tasetbackground', 'to tasetbackground :color \
:shade\ntasetshade :shade\nsetbackground :color\nend\n')
@@ -320,10 +346,11 @@ shade)'))
logo_command='tasetpencolor',
help_string=_('sets color of the line drawn by the \
turtle'))
- self.tw.lc.def_prim('setcolor', 1,
- lambda self, x:
- primitive_dictionary['set']
- ('color', self.tw.canvas.setcolor, x))
+ self.tw.lc.def_prim(
+ 'setcolor',
+ 1,
+ lambda self, x: primitive_dictionary['set'](
+ 'color', self.tw.turtles.get_active_turtle().set_color, x))
palette.add_block('setshade',
style='basic-style-1arg',
@@ -333,10 +360,11 @@ turtle'))
logo_command='tasetshade',
help_string=_('sets shade of the line drawn by the \
turtle'))
- self.tw.lc.def_prim('setshade', 1,
- lambda self, x:
- primitive_dictionary['set']
- ('shade', self.tw.canvas.setshade, x))
+ self.tw.lc.def_prim(
+ 'setshade',
+ 1,
+ lambda self, x: primitive_dictionary['set'](
+ 'shade', self.tw.turtles.get_active_turtle().set_shade, x))
palette.add_block('setgray',
style='basic-style-1arg',
@@ -345,10 +373,11 @@ turtle'))
default=100,
help_string=_('sets gray level of the line drawn by \
the turtle'))
- self.tw.lc.def_prim('setgray', 1,
- lambda self, x:
- primitive_dictionary['set']
- ('gray', self.tw.canvas.setgray, x))
+ self.tw.lc.def_prim(
+ 'setgray',
+ 1,
+ lambda self, x: primitive_dictionary['set'](
+ 'gray', self.tw.turtles.get_active_turtle().set_gray, x))
palette.add_block('color',
style='box-style',
@@ -358,7 +387,10 @@ in place of a number block)'),
value_block=True,
prim_name='color',
logo_command='pencolor')
- self.tw.lc.def_prim('color', 0, lambda self: self.tw.canvas.color)
+ self.tw.lc.def_prim(
+ 'color',
+ 0,
+ lambda self: self.tw.turtles.get_active_turtle().get_color())
palette.add_block('shade',
style='box-style',
@@ -367,7 +399,10 @@ in place of a number block)'),
value_block=True,
prim_name='shade',
logo_command=':shade')
- self.tw.lc.def_prim('shade', 0, lambda self: self.tw.canvas.shade)
+ self.tw.lc.def_prim(
+ 'shade',
+ 0,
+ lambda self: self.tw.turtles.get_active_turtle().get_shade())
palette.add_block('gray',
style='box-style',
@@ -376,7 +411,8 @@ in place of a number block)'),
used in place of a number block)'),
value_block=True,
prim_name='gray')
- self.tw.lc.def_prim('gray', 0, lambda self: self.tw.canvas.gray)
+ self.tw.lc.def_prim('gray', 0, lambda self:
+ self.tw.turtles.get_active_turtle().get_gray())
palette.add_block('penup',
style='basic-style-extended-vertical',
@@ -384,8 +420,11 @@ used in place of a number block)'),
prim_name='penup',
logo_command='penup',
help_string=_('Turtle will not draw when moved.'))
- self.tw.lc.def_prim('penup', 0,
- lambda self: self.tw.canvas.setpen(False))
+ self.tw.lc.def_prim(
+ 'penup',
+ 0,
+ lambda self:
+ self.tw.turtles.get_active_turtle().set_pen_state(False))
palette.add_block('pendown',
style='basic-style-extended-vertical',
@@ -393,8 +432,11 @@ used in place of a number block)'),
prim_name='pendown',
logo_command='pendown',
help_string=_('Turtle will draw when moved.'))
- self.tw.lc.def_prim('pendown', 0,
- lambda self: self.tw.canvas.setpen(True))
+ self.tw.lc.def_prim(
+ 'pendown',
+ 0,
+ lambda self:
+ self.tw.turtles.get_active_turtle().set_pen_state(True))
palette.add_block('setpensize',
style='basic-style-1arg',
@@ -404,12 +446,12 @@ used in place of a number block)'),
logo_command='setpensize',
help_string=_('sets size of the line drawn by the \
turtle'))
- self.tw.lc.def_prim('setpensize', 1,
- lambda self, x:
- primitive_dictionary['set']
- ('pensize', self.tw.canvas.setpensize, x))
- define_logo_function('tasetpensize', 'to tasetpensize :a\nsetpensize \
-round :a\nend\n')
+ self.tw.lc.def_prim(
+ 'setpensize', 1,
+ lambda self, x: primitive_dictionary['set']
+ ('pensize', self.tw.turtles.get_active_turtle().set_pen_size, x))
+ define_logo_function('tasetpensize',
+ 'to tasetpensize :a\nsetpensize round :a\nend\n')
palette.add_block('startfill',
style='basic-style-extended-vertical',
@@ -417,8 +459,10 @@ round :a\nend\n')
prim_name='startfill',
help_string=_('starts filled polygon (used with end \
fill block)'))
- self.tw.lc.def_prim('startfill', 0,
- lambda self: self.tw.canvas.start_fill())
+ self.tw.lc.def_prim(
+ 'startfill',
+ 0,
+ lambda self: self.tw.turtles.get_active_turtle().start_fill())
palette.add_block('stopfill',
style='basic-style-extended-vertical',
@@ -426,8 +470,10 @@ fill block)'))
prim_name='stopfill',
help_string=_('completes filled polygon (used with \
start fill block)'))
- self.tw.lc.def_prim('stopfill', 0,
- lambda self: self.tw.canvas.stop_fill())
+ self.tw.lc.def_prim(
+ 'stopfill',
+ 0,
+ lambda self: self.tw.turtles.get_active_turtle().stop_fill())
palette.add_block('pensize',
style='box-style',
@@ -437,12 +483,15 @@ in place of a number block)'),
value_block=True,
prim_name='pensize',
logo_command='pensize')
- self.tw.lc.def_prim('pensize', 0, lambda self: self.tw.canvas.pensize)
+ self.tw.lc.def_prim(
+ 'pensize',
+ 0,
+ lambda self: self.tw.turtles.get_active_turtle().get_pen_size())
define_logo_function('tapensize', 'to tapensize\noutput first round \
pensize\nend\n')
def _color_palette(self):
- """ The basic Turtle Art color palette """
+ ''' The basic Turtle Art color palette '''
palette = make_palette('colors',
colors=["#00FFFF", "#00A0A0"],
@@ -461,29 +510,6 @@ pensize\nend\n')
self._make_constant(palette, 'white', _('white'), CONSTANTS['white'])
self._make_constant(palette, 'black', _('black'), CONSTANTS['black'])
- # deprecated blocks
- palette.add_block('settextcolor',
- hidden=True,
- style='basic-style-1arg',
- label=_('set text color'),
- prim_name='settextcolor',
- default=0,
- help_string=_('sets color of text drawn by the \
-turtle'))
- self.tw.lc.def_prim('settextcolor', 1,
- lambda self, x: self.tw.canvas.settextcolor(x))
-
- palette.add_block('settextsize',
- hidden=True,
- style='basic-style-1arg',
- label=_('set text size'),
- prim_name='settextsize',
- default=0,
- help_string=_('sets size of text drawn by the \
-turtle'))
- self.tw.lc.def_prim('settextsize', 1,
- lambda self, x: self.tw.canvas.settextsize(x))
-
# In order to map Turtle Art colors to the standard UCB Logo palette,
# we need to define a somewhat complex set of functions.
define_logo_function('tacolor', '\
@@ -558,7 +584,7 @@ make "shade 50 \n\
tasetshade :shade \n')
def _numbers_palette(self):
- """ The basic Turtle Art numbers palette """
+ ''' The basic Turtle Art numbers palette '''
palette = make_palette('numbers',
colors=["#FF00FF", "#A000A0"],
@@ -741,7 +767,7 @@ operators'))
'or', 2, lambda self, x, y: primitive_dictionary['or'](x, y))
def _flow_palette(self):
- """ The basic Turtle Art flow palette """
+ ''' The basic Turtle Art flow palette '''
palette = make_palette('flow',
colors=["#FFC000", "#A08000"],
@@ -841,7 +867,7 @@ boolean operators from Numbers palette'))
lambda self: primitive_dictionary['stopstack']())
def _blocks_palette(self):
- """ The basic Turtle Art blocks palette """
+ ''' The basic Turtle Art blocks palette '''
palette = make_palette('blocks',
colors=["#FFFF00", "#A0A000"],
@@ -1002,7 +1028,7 @@ variable'))
self.tw.lc.def_prim('stack2', 0, primitive_dictionary['stack2'], True)
def _trash_palette(self):
- """ The basic Turtle Art turtle palette """
+ ''' The basic Turtle Art turtle palette '''
palette = make_palette('trash',
colors=["#FFFF00", "#A0A000"],
@@ -1025,22 +1051,32 @@ variable'))
# Block primitives
+ def _prim_clear(self):
+ self.tw.lc.prim_clear()
+ self.tw.turtles.reset_turtles()
+
def _prim_and(self, x, y):
- """ Logical and """
+ ''' Logical and '''
return x & y
def _prim_arc(self, cmd, value1, value2):
- """ Turtle draws an arc of degree, radius """
+ ''' Turtle draws an arc of degree, radius '''
cmd(float(value1), float(value2))
if self.tw.lc.update_values:
self.tw.lc.update_label_value(
- 'xcor', self.tw.canvas.xcor / self.tw.coord_scale)
+ 'xcor',
+ self.tw.turtles.get_active_turtle().get_xy()[0] /
+ self.tw.coord_scale)
+ self.tw.lc.update_label_value(
+ 'ycor',
+ self.tw.turtles.get_active_turtle().get_xy()[1] /
+ self.tw.coord_scale)
self.tw.lc.update_label_value(
- 'ycor', self.tw.canvas.ycor / self.tw.coord_scale)
- self.tw.lc.update_label_value('heading', self.tw.canvas.heading)
+ 'heading',
+ self.tw.turtles.get_active_turtle().get_heading)
def _prim_box(self, x):
- """ Retrieve value from named box """
+ ''' Retrieve value from named box '''
if isinstance(convert(x, float, False), float):
if int(float(x)) == x:
x = int(x)
@@ -1050,7 +1086,7 @@ variable'))
raise logoerror("#emptybox")
def _prim_forever(self, blklist):
- """ Do list forever """
+ ''' Do list forever '''
while True:
self.tw.lc.icall(self.tw.lc.evline, blklist[:])
yield True
@@ -1060,7 +1096,7 @@ variable'))
yield True
def _prim_if(self, boolean, blklist):
- """ If bool, do list """
+ ''' If bool, do list '''
if boolean:
self.tw.lc.icall(self.tw.lc.evline, blklist[:])
yield True
@@ -1068,7 +1104,7 @@ variable'))
yield True
def _prim_ifelse(self, boolean, list1, list2):
- """ If bool, do list1, else do list2 """
+ ''' If bool, do list1, else do list2 '''
if boolean:
self.tw.lc.ijmp(self.tw.lc.evline, list1[:])
yield True
@@ -1078,7 +1114,12 @@ variable'))
def _prim_move(self, cmd, value1, value2=None, pendown=True,
reverse=False):
- """ Turtle moves by method specified in value1 """
+ ''' Turtle moves by method specified in value1 '''
+ pos = None
+ if isinstance(value1, (tuple, list)):
+ pos = value1
+ value1 = pos[0]
+ value2 = pos[1]
if not _num_type(value1):
raise logoerror("#notanumber")
if value2 is None:
@@ -1089,19 +1130,26 @@ variable'))
else:
if not _num_type(value2):
raise logoerror("#notanumber")
- cmd(float(value1), float(value2), pendown=pendown)
+ if pos is not None:
+ cmd((float(value1), float(value2)), pendown=pendown)
+ else:
+ cmd(float(value1), float(value2), pendown=pendown)
if self.tw.lc.update_values:
self.tw.lc.update_label_value(
- 'xcor', self.tw.canvas.xcor / self.tw.coord_scale)
+ 'xcor',
+ self.tw.turtles.get_active_turtle().get_xy()[0] /
+ self.tw.coord_scale)
self.tw.lc.update_label_value(
- 'ycor', self.tw.canvas.ycor / self.tw.coord_scale)
+ 'ycor',
+ self.tw.turtles.get_active_turtle().get_xy()[1] /
+ self.tw.coord_scale)
def _prim_or(self, x, y):
- """ Logical or """
+ ''' Logical or '''
return x | y
def _prim_repeat(self, num, blklist):
- """ Repeat list num times. """
+ ''' Repeat list num times. '''
if not _num_type(num):
raise logoerror("#notanumber")
num = self.tw.lc.int(num)
@@ -1114,25 +1162,27 @@ variable'))
yield True
def _prim_right(self, value, reverse=False):
- """ Turtle rotates clockwise """
+ ''' Turtle rotates clockwise '''
if not _num_type(value):
raise logoerror("#notanumber")
if reverse:
- self.tw.canvas.right(float(-value))
+ self.tw.turtles.get_active_turtle().right(float(-value))
else:
- self.tw.canvas.right(float(value))
+ self.tw.turtles.get_active_turtle().right(float(value))
if self.tw.lc.update_values:
- self.tw.lc.update_label_value('heading', self.tw.canvas.heading)
+ self.tw.lc.update_label_value(
+ 'heading',
+ self.tw.turtles.get_active_turtle().get_heading)
def _prim_set(self, name, cmd, value=None):
- """ Set a value and update the associated value blocks """
+ ''' Set a value and update the associated value blocks '''
if value is not None:
cmd(value)
if self.tw.lc.update_values:
self.tw.lc.update_label_value(name, value)
def _prim_setbox(self, name, x, val):
- """ Define value of named box """
+ ''' Define value of named box '''
if x is not None:
if isinstance(convert(x, float, False), float):
if int(float(x)) == x:
@@ -1146,7 +1196,7 @@ variable'))
self.tw.lc.update_label_value(name, val)
def _prim_stack(self, x):
- """ Process a named stack """
+ ''' Process a named stack '''
if isinstance(convert(x, float, False), float):
if int(float(x)) == x:
x = int(x)
@@ -1161,7 +1211,7 @@ variable'))
yield True
def _prim_stack1(self):
- """ Process Stack 1 """
+ ''' Process Stack 1 '''
if self.tw.lc.stacks['stack1'] is None:
raise logoerror("#nostack")
self.tw.lc.icall(self.tw.lc.evline,
@@ -1172,7 +1222,7 @@ variable'))
yield True
def _prim_stack2(self):
- """ Process Stack 2 """
+ ''' Process Stack 2 '''
if self.tw.lc.stacks['stack2'] is None:
raise logoerror("#nostack")
self.tw.lc.icall(self.tw.lc.evline, self.tw.lc.stacks['stack2'][:])
@@ -1182,29 +1232,29 @@ variable'))
yield True
def _prim_start(self):
- """ Start block: recenter """
+ ''' Start block: recenter '''
if self.tw.running_sugar:
self.tw.activity.recenter()
def _prim_stopstack(self):
- """ Stop execution of a stack """
+ ''' Stop execution of a stack '''
self.tw.lc.procstop = True
def _prim_wait(self, wait_time):
- """ Show the turtle while we wait """
- self.tw.active_turtle.show()
+ ''' Show the turtle while we wait '''
+ self.tw.turtles.get_active_turtle().show()
endtime = _millisecond() + wait_time * 1000.
while _millisecond() < endtime:
sleep(wait_time / 10.)
yield True
- self.tw.active_turtle.hide()
+ self.tw.turtles.get_active_turtle().hide()
self.tw.lc.ireturn()
yield True
# Math primitivies
def _prim_careful_divide(self, x, y):
- """ Raise error on divide by zero """
+ ''' Raise error on divide by zero '''
if isinstance(x, list) and _num_type(y):
z = []
for i in range(len(x)):
@@ -1228,7 +1278,7 @@ variable'))
raise logoerror("#notanumber")
def _prim_equal(self, x, y):
- """ Numeric and logical equal """
+ ''' Numeric and logical equal '''
if isinstance(x, list) and isinstance(y, list):
for i in range(len(x)):
if x[i] != y[i]:
@@ -1250,7 +1300,7 @@ variable'))
raise logoerror("#syntaxerror")
def _prim_less(self, x, y):
- """ Compare numbers and strings """
+ ''' Compare numbers and strings '''
if isinstance(x, list) or isinstance(y, list):
raise logoerror("#syntaxerror")
try:
@@ -1269,11 +1319,11 @@ variable'))
raise logoerror("#notanumber")
def _prim_more(self, x, y):
- """ Compare numbers and strings """
+ ''' Compare numbers and strings '''
return self._prim_less(y, x)
def _prim_plus(self, x, y):
- """ Add numbers, concat strings """
+ ''' Add numbers, concat strings '''
if x in COLORDICT:
x = _color_to_num(x)
if y in COLORDICT:
@@ -1297,7 +1347,7 @@ variable'))
return(xx + yy)
def _prim_minus(self, x, y):
- """ Numerical subtraction """
+ ''' Numerical subtraction '''
if _num_type(x) and _num_type(y):
return(x - y)
elif isinstance(x, list) and isinstance(y, list):
@@ -1311,7 +1361,7 @@ variable'))
raise logoerror("#notanumber")
def _prim_product(self, x, y):
- """ Numerical multiplication """
+ ''' Numerical multiplication '''
if _num_type(x) and _num_type(y):
return(x * y)
elif isinstance(x, list) and _num_type(y):
@@ -1330,7 +1380,7 @@ variable'))
raise logoerror("#notanumber")
def _prim_mod(self, x, y):
- """ Numerical mod """
+ ''' Numerical mod '''
if _num_type(x) and _num_type(y):
return(x % y)
try:
@@ -1341,7 +1391,7 @@ variable'))
raise logoerror("#syntaxerror")
def _prim_sqrt(self, x):
- """ Square root """
+ ''' Square root '''
if _num_type(x):
if x < 0:
raise logoerror("#negroot")
@@ -1354,7 +1404,7 @@ variable'))
raise logoerror("#notanumber")
def _prim_random(self, x, y):
- """ Random integer """
+ ''' Random integer '''
if _num_type(x) and _num_type(y):
return(int(round(uniform(x, y), 0)))
xx, xflag = chr_to_ord(x)
@@ -1371,13 +1421,13 @@ variable'))
raise logoerror("#notanumber")
def _prim_identity(self, x):
- """ Identity function """
+ ''' Identity function '''
return(x)
# Utilities
def _string_to_num(self, x):
- """ Try to comvert a string to a number """
+ ''' Try to comvert a string to a number '''
if isinstance(x, (int, float)):
return(x)
try:
@@ -1399,7 +1449,7 @@ variable'))
raise logoerror("#syntaxerror")
def _make_constant(self, palette, block_name, label, constant):
- """ Factory for constant blocks """
+ ''' Factory for constant blocks '''
if constant in COLORDICT:
if COLORDICT[constant][0] is not None:
value = str(COLORDICT[constant][0])
diff --git a/TurtleArt/tacanvas.py b/TurtleArt/tacanvas.py
index 5eaa4a8..2af41cd 100644
--- a/TurtleArt/tacanvas.py
+++ b/TurtleArt/tacanvas.py
@@ -21,15 +21,13 @@
#THE SOFTWARE.
import gtk
-import gobject
-from math import sin, cos, pi
+from math import pi
import os
import pango
import cairo
import pangocairo
-from tautils import (image_to_base64, get_path, data_to_string, round_int,
- debug_output)
+from tautils import get_path
from taconstants import COLORDICT
@@ -101,38 +99,31 @@ COLOR_TABLE = (
class TurtleGraphics:
''' A class for the Turtle graphics canvas '''
- def __init__(self, tw, width, height):
+ def __init__(self, turtle_window, width, height):
''' Create a sprite to hold the canvas. '''
- self.tw = tw
+ self.turtle_window = turtle_window
self.width = width
self.height = height
+ self.textsize = 48
+ self._fgrgb = [255, 0, 0]
+ self._bgrgb = [255, 248, 222]
+ self._shade = 0
+ self._color = 0
+ self._gray = 100
+ self.cr_svg = None # Surface used for saving to SVG
# Build a cairo.Context from a cairo.XlibSurface
- self.canvas = cairo.Context(self.tw.turtle_canvas)
+ self.canvas = cairo.Context(self.turtle_window.turtle_canvas)
cr = gtk.gdk.CairoContext(self.canvas)
cr.set_line_cap(1) # Set the line cap to be round
- self.cr_svg = None # Surface used for saving to SVG
- self.cx = 0
- self.cy = 0
- self.fgrgb = [255, 0, 0]
- self.bgrgb = [255, 248, 222]
- self.textsize = 48 # deprecated
- self.shade = 0
- self.pendown = False
- self.xcor = 0
- self.ycor = 0
- self.heading = 0
- self.pensize = 5
- self.color = 0
- self.gray = 100
- self.fill = False
- self.poly_points = []
+
+ self.set_pen_size(5)
def setup_svg_surface(self):
''' Set up a surface for saving to SVG '''
- if self.tw.running_sugar:
+ if self.turtle_window.running_sugar:
svg_surface = cairo.SVGSurface(
- os.path.join(get_path(self.tw.activity, 'instance'),
+ os.path.join(get_path(self.turtle_window.activity, 'instance'),
'output.svg'), self.width, self.height)
else:
svg_surface = cairo.SVGSurface(
@@ -141,27 +132,6 @@ class TurtleGraphics:
self.cr_svg = cairo.Context(svg_surface)
self.cr_svg.set_line_cap(1) # Set the line cap to be round
- def start_fill(self):
- ''' Start accumulating points of a polygon to fill. '''
- self.fill = True
- self.poly_points = []
-
- def stop_fill(self):
- ''' Fill the polygon. '''
- self.fill = False
- if len(self.poly_points) == 0:
- return
- self.fill_polygon(self.poly_points)
- if self.tw.sharing():
- shared_poly_points = []
- for p in self.poly_points:
- shared_poly_points.append((self.screen_to_turtle_coordinates
- (p[0], p[1])))
- event = 'F|%s' % (data_to_string([self._get_my_nick(),
- shared_poly_points]))
- self.tw.send_event(event)
- self.poly_points = []
-
def fill_polygon(self, poly_points):
''' Draw the polygon... '''
def _fill_polygon(cr, poly_points):
@@ -188,10 +158,10 @@ class TurtleGraphics:
def _clearscreen(cr):
cr.move_to(0, 0)
- self.bgrgb = [255, 248, 222]
- cr.set_source_rgb(self.bgrgb[0] / 255.,
- self.bgrgb[1] / 255.,
- self.bgrgb[2] / 255.)
+ self._bgrgb = [255, 248, 222]
+ cr.set_source_rgb(self._bgrgb[0] / 255.,
+ self._bgrgb[1] / 255.,
+ self._bgrgb[2] / 255.)
cr.rectangle(0, 0, self.width * 2, self.height * 2)
cr.fill()
@@ -200,359 +170,101 @@ class TurtleGraphics:
if self.cr_svg is not None:
_clearscreen(self.cr_svg)
- self.setpensize(5, share)
- self.setgray(100, share)
- self.setcolor(0, share)
- self.setshade(50, share)
- self.fill = False
- self.poly_points = []
- for turtle_key in iter(self.tw.turtles.dict):
- # Don't reset remote turtles
- if not self.tw.remote_turtle(turtle_key):
- self.set_turtle(turtle_key)
- self.tw.active_turtle.set_color(0)
- self.tw.active_turtle.set_shade(50)
- self.tw.active_turtle.set_gray(100)
- self.tw.active_turtle.set_pen_size(5)
- self.tw.active_turtle.reset_shapes()
- self.seth(0.0, share)
- self.setpen(False, share)
- self.setxy(0.0, 0.0, share)
- self.setpen(True, share)
- self.tw.active_turtle.hide()
- self.set_turtle(self.tw.default_turtle_name)
-
- def forward(self, n, share=True):
- ''' Move the turtle forward.'''
- nn = n * self.tw.coord_scale
- self.canvas.set_source_rgb(self.fgrgb[0] / 255., self.fgrgb[1] / 255.,
- self.fgrgb[2] / 255.)
- if self.cr_svg is not None:
- debug_output('in forward', True)
- self.cr_svg.set_source_rgb(self.fgrgb[0] / 255.,
- self.fgrgb[1] / 255.,
- self.fgrgb[2] / 255.)
- oldx, oldy = self.xcor, self.ycor
- try:
- self.xcor += nn * sin(self.heading * DEGTOR)
- self.ycor += nn * cos(self.heading * DEGTOR)
- except (TypeError, ValueError):
- debug_output('bad value sent to %s' % (__name__),
- self.tw.running_sugar)
- return
- if self.pendown:
- self.draw_line(oldx, oldy, self.xcor, self.ycor)
-
- self.move_turtle()
-
- if self.tw.sharing() and share:
- event = 'f|%s' % (data_to_string([self._get_my_nick(), int(n)]))
- self.tw.send_event(event)
+ def rarc(self, x, y, r, a, heading):
+ ''' draw a clockwise arc '''
+ def _rarc(cr, x, y, r, a, h):
+ cr.arc(x, y, r, (h - 180) * DEGTOR, (h - 180 + a) * DEGTOR)
+ cr.stroke()
+
+ _rarc(self.canvas, x, y, r, a, heading)
self.inval()
- def seth(self, n, share=True):
- ''' Set the turtle heading. '''
- try:
- self.heading = n
- except (TypeError, ValueError):
- debug_output('bad value sent to %s' % (__name__),
- self.tw.running_sugar)
- return
- self.heading %= 360
- self.turn_turtle()
- if self.tw.sharing() and share:
- event = 'r|%s' % (data_to_string([self._get_my_nick(),
- round_int(self.heading)]))
- self.tw.send_event(event)
-
- def right(self, n, share=True):
- ''' Rotate turtle clockwise '''
- try:
- self.heading += n
- except (TypeError, ValueError):
- debug_output('bad value sent to %s' % (__name__),
- self.tw.running_sugar)
- return
- self.heading %= 360
- self.turn_turtle()
- if self.tw.sharing() and share:
- event = 'r|%s' % (data_to_string([self._get_my_nick(),
- round_int(self.heading)]))
- self.tw.send_event(event)
-
- def arc(self, a, r, share=True):
- ''' Draw an arc '''
- self.canvas.set_source_rgb(self.fgrgb[0] / 255., self.fgrgb[1] / 255.,
- self.fgrgb[2] / 255.)
if self.cr_svg is not None:
- self.cr_svg.set_source_rgb(self.fgrgb[0] / 255.,
- self.fgrgb[1] / 255.,
- self.fgrgb[2] / 255.)
- try:
- if a < 0:
- self.larc(-a, r)
- else:
- self.rarc(a, r)
- except (TypeError, ValueError):
- debug_output('bad value sent to %s' % (__name__),
- self.tw.running_sugar)
- return
- self.move_turtle()
- if self.tw.sharing() and share:
- event = 'a|%s' % (data_to_string([self._get_my_nick(),
- [round_int(a), round_int(r)]]))
- self.tw.send_event(event)
-
- def rarc(self, a, r):
- ''' draw a clockwise arc '''
- r *= self.tw.coord_scale
- if r < 0:
- r = -r
- a = -a
- oldx, oldy = self.xcor, self.ycor
- cx = self.xcor + r * cos(self.heading * DEGTOR)
- cy = self.ycor - r * sin(self.heading * DEGTOR)
- if self.pendown:
- x, y = self.turtle_to_screen_coordinates(cx, cy)
-
- def _rarc(cr, x, y, r, a, h):
- cr.arc(x, y, r, (h - 180) * DEGTOR, (h - 180 + a) * DEGTOR)
- cr.stroke()
-
- _rarc(self.canvas, x, y, r, a, self.heading)
- self.inval()
- if self.cr_svg is not None:
- _rarc(self.cr_svg, x, y, r, a, self.heading)
-
- if self.fill:
- if self.poly_points == []:
- self.poly_points.append(('move', x, y))
- self.poly_points.append(('rarc', x, y, r,
- (self.heading - 180) * DEGTOR,
- (self.heading - 180 + a) * DEGTOR))
-
- self.right(a, False)
- self.xcor = cx - r * cos(self.heading * DEGTOR)
- self.ycor = cy + r * sin(self.heading * DEGTOR)
-
- def larc(self, a, r):
+ _rarc(self.cr_svg, x, y, r, a, heading)
+
+ def larc(self, x, y, r, a, heading):
''' draw a counter-clockwise arc '''
- r *= self.tw.coord_scale
- if r < 0:
- r = -r
- a = -a
- oldx, oldy = self.xcor, self.ycor
- cx = self.xcor - r * cos(self.heading * DEGTOR)
- cy = self.ycor + r * sin(self.heading * DEGTOR)
- if self.pendown:
- x, y = self.turtle_to_screen_coordinates(cx, cy)
-
- def _larc(cr, x, y, r, a, h):
- cr.arc_negative(x, y, r, h * DEGTOR, (h - a) * DEGTOR)
- cr.stroke()
-
- _larc(self.canvas, x, y, r, a, self.heading)
- self.inval()
- if self.cr_svg is not None:
- _larc(self.cr_svg, x, y, r, a, self.heading)
-
- if self.fill:
- if self.poly_points == []:
- self.poly_points.append(('move', x, y))
- self.poly_points.append(('larc', x, y, r,
- (self.heading) * DEGTOR,
- (self.heading - a) * DEGTOR))
-
- self.right(-a, False)
- self.xcor = cx + r * cos(self.heading * DEGTOR)
- self.ycor = cy - r * sin(self.heading * DEGTOR)
-
- def setxy(self, x, y, share=True, pendown=True):
- ''' Move turtle to position x,y '''
- oldx, oldy = self.xcor, self.ycor
- x *= self.tw.coord_scale
- y *= self.tw.coord_scale
- try:
- self.xcor, self.ycor = x, y
- except (TypeError, ValueError):
- debug_output('bad value sent to %s' % (__name__),
- self.tw.running_sugar)
- return
-
- if self.pendown and pendown:
- self.canvas.set_source_rgb(self.fgrgb[0] / 255.,
- self.fgrgb[1] / 255.,
- self.fgrgb[2] / 255.)
- if self.cr_svg is not None:
- self.cr_svg.set_source_rgb(self.fgrgb[0] / 255.,
- self.fgrgb[1] / 255.,
- self.fgrgb[2] / 255.)
- self.draw_line(oldx, oldy, self.xcor, self.ycor)
- self.inval()
- self.move_turtle()
-
- if self.tw.sharing() and share:
- event = 'x|%s' % (data_to_string([self._get_my_nick(),
- [round_int(x), round_int(y)]]))
- self.tw.send_event(event)
-
- def setpensize(self, ps, share=True):
- ''' Set the pen size '''
- try:
- if ps < 0:
- ps = 0
- self.pensize = ps
- except (TypeError, ValueError):
- debug_output('bad value sent to %s' % (__name__),
- self.tw.running_sugar)
- return
- self.tw.active_turtle.set_pen_size(ps)
- self.canvas.set_line_width(ps)
- if self.cr_svg is not None:
- self.cr_svg.set_line_width(ps)
- if self.tw.sharing() and share:
- event = 'w|%s' % (data_to_string([self._get_my_nick(),
- round_int(ps)]))
- self.tw.send_event(event)
+ def _larc(cr, x, y, r, a, h):
+ cr.arc_negative(x, y, r, h * DEGTOR, (h - a) * DEGTOR)
+ cr.stroke()
- def setcolor(self, c, share=True):
- ''' Set the pen color '''
+ _larc(self.canvas, x, y, r, a, heading)
+ self.inval()
+ if self.cr_svg is not None:
+ _larc(self.cr_svg, x, y, r, a, heading)
- # Special case for color blocks
- if c in COLORDICT:
- self.setshade(COLORDICT[c][1], share)
- self.setgray(COLORDICT[c][2], share)
- if COLORDICT[c][0] is not None:
- self.setcolor(COLORDICT[c][0], share)
- c = COLORDICT[c][0]
- else:
- c = self.color
-
- try:
- self.color = c
- except (TypeError, ValueError):
- debug_output('bad value sent to %s' % (__name__),
- self.tw.running_sugar)
- return
- self.tw.active_turtle.set_color(c)
- self.set_fgcolor()
- if self.tw.sharing() and share:
- event = 'c|%s' % (data_to_string([self._get_my_nick(),
- round_int(c)]))
- self.tw.send_event(event)
-
- def setgray(self, g, share=True):
- ''' Set the gray level '''
- try:
- self.gray = g
- except (TypeError, ValueError):
- debug_output('bad value sent to %s' % (__name__),
- self.tw.running_sugar)
- return
- if self.gray < 0:
- self.gray = 0
- if self.gray > 100:
- self.gray = 100
- self.set_fgcolor()
- self.tw.active_turtle.set_gray(self.gray)
- if self.tw.sharing() and share:
- event = 'g|%s' % (data_to_string([self._get_my_nick(),
- round_int(self.gray)]))
- self.tw.send_event(event)
-
- def set_textcolor(self):
- ''' Deprecated: Set the text color to foreground color. '''
- return
-
- def settextcolor(self, c): # deprecated
- ''' Set the text color '''
- return
-
- def settextsize(self, c): # deprecated
- ''' Set the text size '''
- try:
- self.tw.textsize = c
- except (TypeError, ValueError):
- debug_output('bad value sent to %s' % (__name__),
- self.tw.running_sugar)
-
- def setshade(self, s, share=True):
- ''' Set the color shade '''
- try:
- self.shade = s
- except (TypeError, ValueError):
- debug_output('bad value sent to %s' % (__name__),
- self.tw.running_sugar)
- return
- self.tw.active_turtle.set_shade(s)
- self.set_fgcolor()
- if self.tw.sharing() and share:
- event = 's|%s' % (data_to_string([self._get_my_nick(),
- round_int(s)]))
- self.tw.send_event(event)
+ def set_pen_size(self, pen_size):
+ ''' Set the pen size '''
+ self.canvas.set_line_width(pen_size)
+ if self.cr_svg is not None:
+ self.cr_svg.set_line_width(pen_size)
def fillscreen(self, c, s):
''' Deprecated method: Fill screen with color/shade '''
- self.fillscreen_with_gray(c, s, self.gray)
+ self.fillscreen_with_gray(c, s, self._gray)
- def fillscreen_with_gray(self, c, s, g):
+ def fillscreen_with_gray(self, color, shade, gray):
''' Fill screen with color/shade/gray and reset to defaults '''
- oldc, olds, oldg = self.color, self.shade, self.gray
+
+ save_rgb = self._fgrgb[:]
# Special case for color blocks
- if c in COLORDICT:
- if COLORDICT[c][0] is None:
- s = COLORDICT[c][1]
- c = self.color
+ if color in COLORDICT:
+ if COLORDICT[color][0] is None:
+ self._shade = COLORDICT[color][1]
else:
- c = COLORDICT[c][0]
- if s in COLORDICT:
- s = COLORDICT[s][1]
- if g in COLORDICT:
- g = COLORDICT[g][2]
+ self._color = COLORDICT[color][0]
+ else:
+ self._color = color
+ if shade in COLORDICT:
+ self._shade = COLORDICT[shade][1]
+ else:
+ self._shade = shade
+ if gray in COLORDICT:
+ self._gray = COLORDICT[gray][2]
+ else:
+ self._gray = gray
+
+ if self._gray < 0:
+ self._gray = 0
+ if self._gray > 100:
+ self._gray = 100
- self.setcolor(c, False)
- self.setshade(s, False)
- self.setgray(g, False)
- self.bgrgb = self.fgrgb[:]
+ self.set_fgcolor(shade=self._shade, gray=self._gray, color=self._color)
+ self._bgrgb = self._fgrgb[:]
def _fillscreen(cr, rgb, w, h):
cr.set_source_rgb(rgb[0] / 255., rgb[1] / 255., rgb[2] / 255.)
cr.rectangle(0, 0, w * 2, h * 2)
cr.fill()
- _fillscreen(self.canvas, self.fgrgb, self.width, self.height)
+ _fillscreen(self.canvas, self._fgrgb, self.width, self.height)
self.inval()
if self.cr_svg is not None:
- _fillscreen(self.cr_svg, self.fgrgb, self.width, self.height)
- self.setcolor(oldc, False)
- self.setshade(olds, False)
- self.setgray(oldg, False)
- self.fill = False
- self.poly_points = []
-
- def set_fgcolor(self):
+ _fillscreen(self.cr_svg, self._fgrgb, self.width, self.height)
+
+ self._fgrgb = save_rgb[:]
+
+ def set_fgcolor(self, shade=None, gray=None, color=None):
''' Set the foreground color '''
- sh = (wrap100(self.shade) - 50) / 50.0
- rgb = COLOR_TABLE[wrap100(self.color)]
+ if shade is not None:
+ self._shade = shade
+ if gray is not None:
+ self._gray = gray
+ if color is not None:
+ self._color = color
+ sh = (wrap100(self._shade) - 50) / 50.0
+ rgb = COLOR_TABLE[wrap100(self._color)]
r = (rgb >> 8) & 0xff00
- r = calc_gray(r, self.gray)
+ r = calc_gray(r, self._gray)
r = calc_shade(r, sh)
g = rgb & 0xff00
- g = calc_gray(g, self.gray)
+ g = calc_gray(g, self._gray)
g = calc_shade(g, sh)
b = (rgb << 8) & 0xff00
- b = calc_gray(b, self.gray)
+ b = calc_gray(b, self._gray)
b = calc_shade(b, sh)
- self.fgrgb = [r >> 8, g >> 8, b >> 8]
-
- def setpen(self, bool, share=True):
- ''' Lower or raise the pen '''
- self.pendown = bool
- self.tw.active_turtle.set_pen_state(bool)
- if self.tw.sharing() and share:
- event = 'p|%s' % (data_to_string([self._get_my_nick(), bool]))
- self.tw.send_event(event)
+ self._fgrgb = [r >> 8, g >> 8, b >> 8]
def draw_surface(self, surface, x, y, w, h):
''' Draw a surface '''
@@ -568,7 +280,7 @@ class TurtleGraphics:
if self.cr_svg is not None:
_draw_surface(self.cr_svg, surface, x, y, w, h)
- def draw_pixbuf(self, pixbuf, a, b, x, y, w, h, path, share=True):
+ def draw_pixbuf(self, pixbuf, a, b, x, y, w, h, heading):
''' Draw a pixbuf '''
def _draw_pixbuf(cr, pixbuf, a, b, x, y, w, h, heading):
@@ -585,37 +297,15 @@ class TurtleGraphics:
cc.fill()
cc.restore()
- _draw_pixbuf(self.canvas, pixbuf, a, b, x, y, w, h, self.heading)
+ _draw_pixbuf(self.canvas, pixbuf, a, b, x, y, w, h, heading)
self.inval()
if self.cr_svg is not None:
- _draw_pixbuf(self.cr_svg, pixbuf, a, b, x, y, w, h, self.heading)
- if self.tw.sharing() and share:
- if self.tw.running_sugar:
- tmp_path = get_path(self.tw.activity, 'instance')
- else:
- tmp_path = '/tmp'
- tmp_file = os.path.join(get_path(self.tw.activity, 'instance'),
- 'tmpfile.png')
- pixbuf.save(tmp_file, 'png', {'quality': '100'})
- data = image_to_base64(tmp_file, tmp_path)
- height = pixbuf.get_height()
- width = pixbuf.get_width()
- x, y = self.screen_to_turtle_coordinates(x, y)
- event = 'P|%s' % (data_to_string([self._get_my_nick(),
- [round_int(a), round_int(b),
- round_int(x), round_int(y),
- round_int(w), round_int(h),
- round_int(width),
- round_int(height),
- data]]))
- gobject.idle_add(self.tw.send_event, event)
- os.remove(tmp_file)
-
- def draw_text(self, label, x, y, size, w, share=True):
+ _draw_pixbuf(self.cr_svg, pixbuf, a, b, x, y, w, h, heading)
+
+ def draw_text(self, label, x, y, size, width, heading, scale):
''' Draw text '''
- w *= self.tw.coord_scale
- def _draw_text(cr, label, x, y, size, w, scale, heading, rgb):
+ def _draw_text(cr, label, x, y, size, width, scale, heading, rgb):
cc = pangocairo.CairoContext(cr)
pl = cc.create_layout()
fd = pango.FontDescription('Sans')
@@ -627,7 +317,7 @@ class TurtleGraphics:
pl.set_text(str(label))
else:
pl.set_text(str(label))
- pl.set_width(int(w) * pango.SCALE)
+ pl.set_width(int(width) * pango.SCALE)
cc.save()
cc.translate(x, y)
cc.rotate(heading * DEGTOR)
@@ -636,36 +326,24 @@ class TurtleGraphics:
cc.show_layout(pl)
cc.restore()
- _draw_text(self.canvas, label, x, y, size, w, self.tw.coord_scale,
- self.heading, self.fgrgb)
+ width *= scale
+ _draw_text(self.canvas, label, x, y, size, width, scale, heading,
+ self._fgrgb)
self.inval()
if self.cr_svg is not None: # and self.pendown:
- _draw_text(self.cr_svg, label, x, y, size, w, self.tw.coord_scale,
- self.heading, self.fgrgb)
- if self.tw.sharing() and share:
- event = 'W|%s' % (data_to_string([self._get_my_nick(),
- [label, round_int(x),
- round_int(y), round_int(size),
- round_int(w)]]))
- self.tw.send_event(event)
-
- def turtle_to_screen_coordinates(self, x, y):
- ''' The origin of turtle coordinates is the center of the screen '''
- return self.width / 2. + x, self.invert_y_coordinate(y)
-
- def screen_to_turtle_coordinates(self, x, y):
- ''' The origin of the screen coordinates is the upper left corner '''
- return x - self.width / 2., self.invert_y_coordinate(y)
-
- def invert_y_coordinate(self, y):
- ''' Positive y goes up in turtle coordinates, down in sceeen
- coordinates '''
- return self.height / 2. - y
+ _draw_text(self.cr_svg, label, x, y, size, width, scale, heading,
+ self._fgrgb)
+
+ def set_source_rgb(self):
+ r = self._fgrgb[0] / 255.
+ g = self._fgrgb[1] / 255.
+ b = self._fgrgb[2] / 255.
+ self.canvas.set_source_rgb(r, g, b)
+ if self.cr_svg is not None:
+ self.cr_svg.set_source_rgb(r, g, b)
def draw_line(self, x1, y1, x2, y2):
''' Draw a line '''
- x1, y1 = self.turtle_to_screen_coordinates(x1, y1)
- x2, y2 = self.turtle_to_screen_coordinates(x2, y2)
def _draw_line(cr, x1, y1, x2, y2):
cr.move_to(x1, y1)
@@ -675,40 +353,23 @@ class TurtleGraphics:
_draw_line(self.canvas, x1, y1, x2, y2)
if self.cr_svg is not None:
_draw_line(self.cr_svg, x1, y1, x2, y2)
- if self.fill:
- if self.poly_points == []:
- self.poly_points.append(('move', x1, y1))
- self.poly_points.append(('line', x2, y2))
-
- def turn_turtle(self):
- ''' Change the orientation of the turtle '''
- self.tw.active_turtle.set_heading(self.heading)
-
- def move_turtle(self):
- ''' Move the turtle '''
- x, y = self.turtle_to_screen_coordinates(self.xcor, self.ycor)
- if self.tw.interactive_mode:
- self.tw.active_turtle.move(
- (self.cx + x - self.tw.active_turtle.spr.rect.width / 2.,
- self.cy + y - self.tw.active_turtle.spr.rect.height / 2.))
- else:
- self.tw.active_turtle.move((self.cx + x, self.cy + y))
+ self.inval()
def get_color_index(self, r, g, b, a=0):
''' Find the closest palette entry to the rgb triplet '''
- if self.shade != 50 or self.gray != 100:
+ if self._shade != 50 or self._gray != 100:
r <<= 8
g <<= 8
b <<= 8
- if self.shade != 50:
- sh = (wrap100(self.shade) - 50) / 50.
+ if self._shade != 50:
+ sh = (wrap100(self._shade) - 50) / 50.
r = calc_shade(r, sh, True)
g = calc_shade(g, sh, True)
b = calc_shade(b, sh, True)
- if self.gray != 100:
- r = calc_gray(r, self.gray, True)
- g = calc_gray(g, self.gray, True)
- b = calc_gray(b, self.gray, True)
+ if self._gray != 100:
+ r = calc_gray(r, self._gray, True)
+ g = calc_gray(g, self._gray, True)
+ b = calc_gray(b, self._gray, True)
r >>= 8
g >>= 8
b >>= 8
@@ -727,20 +388,19 @@ class TurtleGraphics:
closest_color = i
return closest_color
- def get_pixel(self):
+ def get_pixel(self, x, y):
''' Read the pixel at x, y '''
- if self.tw.interactive_mode:
- x, y = self.turtle_to_screen_coordinates(self.xcor, self.ycor)
+ if self.turtle_window.interactive_mode:
x = int(x)
y = int(y)
- w = self.tw.turtle_canvas.get_width()
- h = self.tw.turtle_canvas.get_height()
+ w = self.turtle_window.turtle_canvas.get_width()
+ h = self.turtle_window.turtle_canvas.get_height()
if x < 0 or x > (w - 1) or y < 0 or y > (h - 1):
return(-1, -1, -1, -1)
# create a new 1x1 cairo surface
cs = cairo.ImageSurface(cairo.FORMAT_RGB24, 1, 1)
cr = cairo.Context(cs)
- cr.set_source_surface(self.tw.turtle_canvas, -x, -y)
+ cr.set_source_surface(self.turtle_window.turtle_canvas, -x, -y)
cr.rectangle(0, 0, 1, 1)
cr.set_operator(cairo.OPERATOR_SOURCE)
cr.fill()
@@ -750,32 +410,6 @@ class TurtleGraphics:
else:
return(-1, -1, -1, -1)
- def set_turtle(self, k, colors=None):
- ''' Select the current turtle and associated pen status '''
- if k not in self.tw.turtles.dict:
- # if it is a new turtle, start it in the center of the screen
- self.tw.active_turtle = self.tw.turtles.get_turtle(k, True, colors)
- self.seth(0.0, False)
- self.setxy(0.0, 0.0, False, pendown=False)
- self.tw.active_turtle.set_pen_state(True)
- elif colors is not None:
- self.tw.active_turtle = self.tw.turtles.get_turtle(k, False)
- self.tw.active_turtle.set_turtle_colors(colors)
- else:
- self.tw.active_turtle = self.tw.turtles.get_turtle(k, False)
- self.tw.active_turtle.show()
- tx, ty = self.tw.active_turtle.get_xy()
- self.xcor, self.ycor = self.screen_to_turtle_coordinates(tx, ty)
- if self.tw.interactive_mode:
- self.xcor += self.tw.active_turtle.spr.rect.width / 2.
- self.ycor -= self.tw.active_turtle.spr.rect.height / 2.
- self.heading = self.tw.active_turtle.get_heading()
- self.setcolor(self.tw.active_turtle.get_color(), False)
- self.setgray(self.tw.active_turtle.get_gray(), False)
- self.setshade(self.tw.active_turtle.get_shade(), False)
- self.setpensize(self.tw.active_turtle.get_pen_size(), False)
- self.setpen(self.tw.active_turtle.get_pen_state(), False)
-
def svg_close(self):
''' Close current SVG graphic '''
self.cr_svg.show_page()
@@ -784,9 +418,6 @@ class TurtleGraphics:
''' Reset svg flags '''
self.cr_svg = None
- def _get_my_nick(self):
- return self.tw.nick
-
def inval(self):
''' Invalidate a region for gtk '''
- self.tw.inval_all()
+ self.turtle_window.inval_all()
diff --git a/TurtleArt/tacollaboration.py b/TurtleArt/tacollaboration.py
index 32df65b..26a21d7 100644
--- a/TurtleArt/tacollaboration.py
+++ b/TurtleArt/tacollaboration.py
@@ -200,7 +200,7 @@ class Collaboration():
return
# Save active Turtle
- save_active_turtle = self._tw.active_turtle
+ save_active_turtle = self._tw.turtles.get_active_turtle()
try:
command, payload = event_message.split('|', 2)
@@ -211,7 +211,7 @@ class Collaboration():
self._processing_methods[command](payload)
# Restore active Turtle
- self._tw.canvas.set_turtle(
+ self._tw.turtles.set_turtle(
self._tw.turtles.get_turtle_key(save_active_turtle))
def send_event(self, entry):
@@ -229,13 +229,13 @@ class Collaboration():
# Make sure it is not a "rejoin".
if not nick in self._tw.remote_turtle_dictionary:
# Add new turtle for the joiner.
- self._tw.canvas.set_turtle(nick, colors)
+ self._tw.turtles.set_turtle(nick, colors)
self._tw.label_remote_turtle(nick, colors)
self._tw.remote_turtle_dictionary[nick] = colors
else:
self._tw.remote_turtle_dictionary = self._get_dictionary()
# Add new turtle for the joiner.
- self._tw.canvas.set_turtle(nick, colors)
+ self._tw.turtles.set_turtle(nick, colors)
self._tw.label_remote_turtle(nick, colors)
# Sharer should send the updated remote turtle dictionary to everyone.
@@ -264,7 +264,7 @@ class Collaboration():
# Add new the turtle.
colors = remote_turtle_dictionary[nick]
self._tw.remote_turtle_dictionary[nick] = colors
- self._tw.canvas.set_turtle(nick, colors)
+ self._tw.turtles.set_turtle(nick, colors)
# Label the remote turtle.
self._tw.label_remote_turtle(nick, colors)
debug_output('adding %s to remote turtle dictionary' %
@@ -278,23 +278,23 @@ class Collaboration():
def send_my_xy(self):
''' Set xy location so joiner can sync turtle positions. Should be
used to sync positions after turtle drag. '''
- self._tw.canvas.set_turtle(self._get_nick())
- if self._tw.canvas.pendown:
+ self._tw.turtles.set_turtle(self._get_nick())
+ if self._tw.turtles.get_active_turtle().get_pen_state():
self.send_event('p|%s' % (data_to_string([self._get_nick(),
False])))
put_pen_back_down = True
else:
put_pen_back_down = False
- self.send_event('x|%s' %
- (data_to_string([self._get_nick(),
- [int(self._tw.canvas.xcor),
- int(self._tw.canvas.ycor)]])))
+ self.send_event('x|%s' % (data_to_string(
+ [self._get_nick(),
+ [int(self._tw.turtles.get_active_turtle().get_xy()[0]),
+ int(self._tw.turtles.get_active_turtle().get_xy()[1])]])))
if put_pen_back_down:
self.send_event('p|%s' % (data_to_string([self._get_nick(),
True])))
- self.send_event('r|%s' %
- (data_to_string([self._get_nick(),
- int(self._tw.canvas.heading)])))
+ self.send_event('r|%s' % (data_to_string(
+ [self._get_nick(),
+ int(self._tw.turtles.get_active_turtle().get_heading())])))
def _reskin_turtle(self, payload):
if len(payload) > 0:
@@ -307,8 +307,8 @@ class Collaboration():
file_name = base64_to_image(data, tmp_path)
pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(file_name,
width, height)
- self._tw.canvas.set_turtle(nick)
- self._tw.active_turtle.set_shapes([pixbuf])
+ self._tw.turtles.set_turtle(nick)
+ self._tw.turtles.get_active_turtle().set_shapes([pixbuf])
def _draw_pixbuf(self, payload):
if len(payload) > 0:
@@ -322,89 +322,93 @@ class Collaboration():
file_name = base64_to_image(data, tmp_path)
pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(file_name,
width, height)
- x, y = self._tw.canvas.turtle_to_screen_coordinates(x, y)
- self._tw.canvas.draw_pixbuf(pixbuf, a, b, x, y, w, h,
- file_name, False)
+ pos = self._tw.turtles.turtle_to_screen_coordinates((x, y))
+ self._tw.turtles.get_active_turtle().draw_pixbuf(
+ pixbuf, a, b, pos[0], pos[1], w, h, file_name, False)
def _move_forward(self, payload):
if len(payload) > 0:
[nick, x] = data_from_string(payload)
if nick != self._tw.nick:
- self._tw.canvas.set_turtle(nick)
- self._tw.canvas.forward(x, False)
+ self._tw.turtles.set_turtle(nick)
+ self._tw.turtles.get_active_turtle().forward(x, False)
def _move_in_arc(self, payload):
if len(payload) > 0:
[nick, [a, r]] = data_from_string(payload)
if nick != self._tw.nick:
- self._tw.canvas.set_turtle(nick)
- self._tw.canvas.arc(a, r, False)
+ self._tw.turtles.set_turtle(nick)
+ self._tw.turtles.get_active_turtle().arc(a, r, False)
def _rotate_turtle(self, payload):
if len(payload) > 0:
[nick, h] = data_from_string(payload)
if nick != self._tw.nick:
- self._tw.canvas.set_turtle(nick)
- self._tw.canvas.seth(h, False)
+ self._tw.turtles.set_turtle(nick)
+ self._tw.turtles.get_active_turtle().set_heading(h, False)
def _setxy(self, payload):
if len(payload) > 0:
[nick, [x, y]] = data_from_string(payload)
if nick != self._tw.nick:
- self._tw.canvas.set_turtle(nick)
- self._tw.canvas.setxy(x, y, False)
+ self._tw.turtles.set_turtle(nick)
+ self._tw.turtles.get_active_turtle().set_xy(x, y, False)
def _draw_text(self, payload):
if len(payload) > 0:
[nick, [label, x, y, size, w]] = data_from_string(payload)
if nick != self._tw.nick:
- self._tw.canvas.draw_text(label, x, y, size, w, False)
+ self._tw.turtles.get_active_turtle().draw_text(
+ label, x, y, size, w, False)
def _set_pen_color(self, payload):
if len(payload) > 0:
[nick, x] = data_from_string(payload)
if nick != self._tw.nick:
- self._tw.canvas.set_turtle(nick)
- self._tw.canvas.setcolor(x, False)
+ self._tw.turtles.set_turtle(nick)
+ self._tw.turtles.get_active_turtle().set_color(x, False)
def _set_pen_gray_level(self, payload):
if len(payload) > 0:
[nick, x] = data_from_string(payload)
if nick != self._tw.nick:
- self._tw.canvas.set_turtle(nick)
- self._tw.canvas.setgray(x, False)
+ self._tw.turtles.set_turtle(nick)
+ self._tw.turtles.get_active_turtle().set_gray(x, False)
def _set_pen_shade(self, payload):
if len(payload) > 0:
[nick, x] = data_from_string(payload)
if nick != self._tw.nick:
- self._tw.canvas.set_turtle(nick)
- self._tw.canvas.setshade(x, False)
+ self._tw.turtles.set_turtle(nick)
+ self._tw.turtles.get_active_turtle().set_shade(x, False)
def _set_pen_width(self, payload):
if len(payload) > 0:
[nick, x] = data_from_string(payload)
if nick != self._tw.nick:
- self._tw.canvas.set_turtle(nick)
- self._tw.canvas.setpensize(x, False)
+ self._tw.turtles.set_turtle(nick)
+ self._tw.turtles.get_active_turtle().set_pen_size(x, False)
def _set_pen_state(self, payload):
if len(payload) > 0:
[nick, x] = data_from_string(payload)
if nick != self._tw.nick:
- self._tw.canvas.set_turtle(nick)
- self._tw.canvas.setpen(x, False)
+ self._tw.turtles.set_turtle(nick)
+ self._tw.turtles.get_active_turtle().set_pen_state(x, False)
def _fill_polygon(self, payload):
- # Check to make sure that the poly_point array is passed properly
if len(payload) > 0:
[nick, poly_points] = data_from_string(payload)
shared_poly_points = []
for i in range(len(poly_points)):
shared_poly_points.append(
- (self._tw.canvas.turtle_to_screen_coordinates
+ (self._tw.turtles.turtle_to_screen_coordinates
(poly_points[i][0], poly_points[i][1])))
- self._tw.canvas.fill_polygon(shared_poly_points)
+ if nick != self._tw.nick:
+ self._tw.turtles.set_turtle(nick)
+ self._tw.turtles.get_active_turtle().set_poly_points(
+ shared_poly_points)
+ self._tw.turtles.get_active_turtle().stop_fill(False)
def _speak(self, payload):
if len(payload) > 0:
diff --git a/TurtleArt/talogo.py b/TurtleArt/talogo.py
index 29d05d9..646b028 100644
--- a/TurtleArt/talogo.py
+++ b/TurtleArt/talogo.py
@@ -166,7 +166,7 @@ class LogoCode:
if self.tw.gst_available:
from tagplay import stop_media
stop_media(self)
- self.tw.active_turtle.show()
+ self.tw.turtles.get_active_turtle().show()
self.tw.running_blocks = False
# If we disabled hover help, reenable it
if self._disable_help:
@@ -188,10 +188,14 @@ class LogoCode:
return sym
def run_blocks(self, code):
+ """Run code generated by generate_code().
+ """
self.start_time = time()
self._setup_cmd(code)
def generate_code(self, blk, blocks):
+ """ Generate code to be passed to run_blocks() from a stack of blocks.
+ """
for k in self.stacks.keys():
self.stacks[k] = None
self.stacks['stack1'] = None
@@ -323,7 +327,7 @@ class LogoCode:
def _setup_cmd(self, string):
""" Execute the psuedocode. """
- self.hidden_turtle = self.tw.active_turtle
+ self.hidden_turtle = self.tw.turtles.get_active_turtle()
self.hidden_turtle.hide() # Hide the turtle while we are running.
self.procstop = False
blklist = self._readline(string)
@@ -413,12 +417,12 @@ class LogoCode:
# In debugging modes, we pause between steps and show the turtle.
if self.tw.step_time > 0:
- self.tw.active_turtle.show()
+ self.tw.turtles.get_active_turtle().show()
endtime = _millisecond() + self.tw.step_time * 100.
while _millisecond() < endtime:
sleep(0.1)
yield True
- self.tw.active_turtle.hide()
+ self.tw.turtles.get_active_turtle().hide()
# 'Stand-alone' booleans are handled here.
if token == self.symopar:
@@ -537,7 +541,7 @@ class LogoCode:
self.hidden_turtle.show()
self.hidden_turtle = None
else:
- self.tw.active_turtle.show()
+ self.tw.turtles.get_active_turtle().show()
self.tw.running_blocks = False
return False
except logoerror, e:
@@ -599,6 +603,7 @@ class LogoCode:
from tagplay import stop_media
stop_media(self)
self.tw.canvas.clearscreen()
+ self.tw.turtles.reset_turtles()
self.scale = DEFAULT_SCALE
self.hidden_turtle = None
self.start_time = time()
@@ -689,11 +694,13 @@ class LogoCode:
def x2tx(self):
""" Convert screen coordinates to turtle coordinates """
- return int(self.tw.canvas.width / 2) + int(self.tw.canvas.xcor)
+ return int(self.tw.canvas.width / 2) + \
+ int(self.tw.turtles.get_active_turtle().get_xy()[0])
def y2ty(self):
""" Convert screen coordinates to turtle coordinates """
- return int(self.tw.canvas.height / 2) - int(self.tw.canvas.ycor)
+ return int(self.tw.canvas.height / 2) - \
+ int(self.tw.turtles.get_active_turtle().get_xy()[1])
def wpercent(self):
""" width as a percentage of screen coordinates """
@@ -742,20 +749,23 @@ class LogoCode:
w *= self.tw.coord_scale
h *= self.tw.coord_scale
if center:
- self.tw.canvas.draw_pixbuf(self.pixbuf, 0, 0,
- self.x2tx() - int(w / 2),
- self.y2ty() - int(h / 2), w, h,
- self.filepath)
+ self.tw.turtles.get_active_turtle().draw_pixbuf(
+ self.pixbuf, 0, 0,
+ self.x2tx() - int(w / 2),
+ self.y2ty() - int(h / 2), w, h,
+ self.filepath)
elif offset:
- self.tw.canvas.draw_pixbuf(self.pixbuf, 0, 0,
- self.x2tx(),
- self.y2ty() - h,
- w, h, self.filepath)
+ self.tw.turtles.get_active_turtle().draw_pixbuf(
+ self.pixbuf, 0, 0,
+ self.x2tx(),
+ self.y2ty() - h,
+ w, h, self.filepath)
else:
- self.tw.canvas.draw_pixbuf(self.pixbuf, 0, 0,
- self.x2tx(),
- self.y2ty(),
- w, h, self.filepath)
+ self.tw.turtles.get_active_turtle().draw_pixbuf(
+ self.pixbuf, 0, 0,
+ self.x2tx(),
+ self.y2ty(),
+ w, h, self.filepath)
def insert_desc(self, mimetype=None, description=None):
""" Description text only (at current x, y) """
@@ -786,8 +796,8 @@ class LogoCode:
else:
text = self.filepath
if text is not None:
- self.tw.canvas.draw_text(text, self.x2tx(), self.y2ty(),
- self.body_height, w)
+ self.tw.turtles.get_active_turtle().draw_text(
+ text, self.x2tx(), self.y2ty(), self.body_height, w)
def media_wait(self):
""" Wait for media to stop playing """
@@ -849,7 +859,9 @@ class LogoCode:
def _expand_forever(self, b, blk, blocks):
""" Expand a while or until block into: forever, ifelse, stopstack
- Expand a forever block to run in a separate stack """
+ Expand a forever block to run in a separate stack
+ Parameters: the loop block, the top block, all blocks.
+ Return the start block of the expanded loop, and all blocks."""
# TODO: create a less brittle way of doing this; having to
# manage the connections and flows locally means we may run
diff --git a/TurtleArt/taturtle.py b/TurtleArt/taturtle.py
index 7db72e6..12882db 100644
--- a/TurtleArt/taturtle.py
+++ b/TurtleArt/taturtle.py
@@ -19,22 +19,29 @@
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
#THE SOFTWARE.
-from random import uniform
-from math import sin, cos, pi, sqrt
+import os
+
import gtk
+import gobject
import cairo
-from taconstants import (TURTLE_LAYER, DEFAULT_TURTLE_COLORS)
-from tasprite_factory import (SVG, svg_str_to_pixbuf)
-from tacanvas import (wrap100, COLOR_TABLE)
+from random import uniform
+from math import sin, cos, pi, sqrt
+from taconstants import (TURTLE_LAYER, DEFAULT_TURTLE_COLORS, DEFAULT_TURTLE,
+ COLORDICT)
+from tasprite_factory import SVG, svg_str_to_pixbuf
+from tacanvas import wrap100, COLOR_TABLE
from sprites import Sprite
-from tautils import debug_output
+from tautils import (debug_output, data_to_string, round_int, get_path,
+ image_to_base64)
SHAPES = 36
+DEGTOR = pi / 180.
+RTODEG = 180. / pi
def generate_turtle_pixbufs(colors):
- """ Generate pixbufs for generic turtles """
+ ''' Generate pixbufs for generic turtles '''
shapes = []
svg = SVG()
svg.set_scale(1.0)
@@ -46,106 +53,196 @@ def generate_turtle_pixbufs(colors):
class Turtles:
- def __init__(self, sprite_list):
- """ Class to hold turtles """
- self.dict = dict()
- self.sprite_list = sprite_list
- self.default_pixbufs = []
-
- def get_turtle(self, k, append=False, colors=None):
- """ Find a turtle """
- if k in self.dict:
- return self.dict[k]
+ def __init__(self, turtle_window):
+ ''' Class to hold turtles '''
+ self.turtle_window = turtle_window
+ self.sprite_list = turtle_window.sprite_list
+ self.width = turtle_window.width
+ self.height = turtle_window.height
+ self.dict = {}
+ self._default_pixbufs = []
+ self._active_turtle = None
+ self._default_turtle_name = DEFAULT_TURTLE
+
+ def get_turtle(self, turtle_name, append=False, colors=None):
+ ''' Find a turtle '''
+ if turtle_name in self.dict:
+ return self.dict[turtle_name]
elif not append:
return None
else:
if colors is None:
- Turtle(self, k)
+ Turtle(self, turtle_name)
elif isinstance(colors, (list, tuple)):
- Turtle(self, k, colors)
+ Turtle(self, turtle_name, colors)
else:
- Turtle(self, k, colors.split(','))
- return self.dict[k]
+ Turtle(self, turtle_name, colors.split(','))
+ return self.dict[turtle_name]
def get_turtle_key(self, turtle):
- """ Find a turtle's name """
- for k in iter(self.dict):
- if self.dict[k] == turtle:
- return k
+ ''' Find a turtle's name '''
+ for turtle_name in iter(self.dict):
+ if self.dict[turtle_name] == turtle:
+ return turtle_name
return None
def turtle_count(self):
- """ How many turtles are there? """
+ ''' How many turtles are there? '''
return(len(self.dict))
- def add_to_dict(self, k, turtle):
- """ Add a new turtle """
- self.dict[k] = turtle
+ def add_to_dict(self, turtle_name, turtle):
+ ''' Add a new turtle '''
+ self.dict[turtle_name] = turtle
- def remove_from_dict(self, k):
- """ Delete a turtle """
- if k in self.dict:
- del(self.dict[k])
+ def remove_from_dict(self, turtle_name):
+ ''' Delete a turtle '''
+ if turtle_name in self.dict:
+ del(self.dict[turtle_name])
def show_all(self):
- """ Make all turtles visible """
- for k in iter(self.dict):
- self.dict[k].show()
+ ''' Make all turtles visible '''
+ for turtle_name in iter(self.dict):
+ self.dict[turtle_name].show()
def spr_to_turtle(self, spr):
- """ Find the turtle that corresponds to sprite spr. """
- for k in iter(self.dict):
- if spr == self.dict[k].spr:
- return self.dict[k]
+ ''' Find the turtle that corresponds to sprite spr. '''
+ for turtle_name in iter(self.dict):
+ if spr == self.dict[turtle_name].spr:
+ return self.dict[turtle_name]
return None
def get_pixbufs(self):
- """ Get the pixbufs for the default turtle shapes. """
- if self.default_pixbufs == []:
- self.default_pixbufs = generate_turtle_pixbufs(
+ ''' Get the pixbufs for the default turtle shapes. '''
+ if self._default_pixbufs == []:
+ self._default_pixbufs = generate_turtle_pixbufs(
["#008000", "#00A000"])
- return(self.default_pixbufs)
+ return(self._default_pixbufs)
+
+ def turtle_to_screen_coordinates(self, pos):
+ ''' The origin of turtle coordinates is the center of the screen '''
+ return [self.width / 2.0 + pos[0], self._invert_y_coordinate(pos[1])]
+
+ def screen_to_turtle_coordinates(self, pos):
+ ''' The origin of the screen coordinates is the upper-left corner '''
+ return [pos[0] - self.width / 2.0, self._invert_y_coordinate(pos[1])]
+
+ def _invert_y_coordinate(self, y):
+ ''' Positive y goes up in turtle coordinates, down in sceeen
+ coordinates '''
+ return self.height / 2.0 - y
+
+ def reset_turtles(self):
+ for turtle_name in iter(self.dict):
+ self.set_turtle(turtle_name)
+ if not self._active_turtle.get_remote():
+ self._active_turtle.set_color(0)
+ self._active_turtle.set_shade(50)
+ self._active_turtle.set_gray(100)
+ self._active_turtle.set_pen_size(5)
+ self._active_turtle.reset_shapes()
+ self._active_turtle.set_heading(0.0)
+ self._active_turtle.set_pen_state(False)
+ self._active_turtle.move_turtle((0.0, 0.0))
+ self._active_turtle.set_pen_state(True)
+ self._active_turtle.set_fill(False)
+ self._active_turtle.hide()
+ self.set_turtle(self._default_turtle_name)
+
+ def set_turtle(self, turtle_name, colors=None):
+ ''' Select the current turtle and associated pen status '''
+ if turtle_name not in self.dict:
+ # if it is a new turtle, start it in the center of the screen
+ self._active_turtle = self.get_turtle(turtle_name, True, colors)
+ self._active_turtle.set_heading(0.0, False)
+ self._active_turtle.set_xy((0.0, 0.0), False, pendown=False)
+ self._active_turtle.set_pen_state(True)
+ elif colors is not None:
+ self._active_turtle = self.get_turtle(turtle_name, False)
+ self._active_turtle.set_turtle_colors(colors)
+ else:
+ self._active_turtle = self.get_turtle(turtle_name, False)
+ self._active_turtle.show()
+ self._active_turtle.set_color(share=False)
+ self._active_turtle.set_gray(share=False)
+ self._active_turtle.set_shade(share=False)
+ self._active_turtle.set_pen_size(share=False)
+ self._active_turtle.set_pen_state(share=False)
+
+ def set_default_turtle_name(self, name):
+ self._default_turtle_name = name
+
+ def get_default_turtle_name(self):
+ return self._default_turtle_name
+
+ def set_active_turtle(self, active_turtle):
+ self._active_turtle = active_turtle
+
+ def get_active_turtle(self):
+ return self._active_turtle
class Turtle:
- def __init__(self, turtles, key, turtle_colors=None):
- """ The turtle is not a block, just a sprite with an orientation """
- self.x = 0.0
- self.y = 0.0
- self.hidden = False
- self.shapes = []
- self.custom_shapes = False
- self.type = 'turtle'
- self.name = key
- self.heading = 0.0
- self.pen_shade = 50
- self.pen_color = 0
- self.pen_gray = 100
- self.pen_size = 5
- self.pen_state = True
+ def __init__(self, turtles, turtle_name, turtle_colors=None):
+ ''' The turtle is not a block, just a sprite with an orientation '''
+ self.spr = None
self.label_block = None
+ self._turtles = turtles
+ self._shapes = []
+ self._custom_shapes = False
+ self._name = turtle_name
+ self._hidden = False
+ self._remote = False
+ self._x = 0.0
+ self._y = 0.0
+ self._heading = 0.0
+ self._half_width = 0
+ self._half_height = 0
+ self._drag_radius = None
+ self._pen_shade = 50
+ self._pen_color = 0
+ self._pen_gray = 100
+ self._pen_size = 5
+ self._pen_state = True
+ self._pen_fill = False
+ self._poly_points = []
+
+ self._prep_shapes(turtle_name, self._turtles, turtle_colors)
+
+ # Create a sprite for the turtle in interactive mode.
+ if turtles.sprite_list is not None:
+ self.spr = Sprite(self._turtles.sprite_list, 0, 0, self._shapes[0])
- self._prep_shapes(key, turtles, turtle_colors)
+ self._calculate_sizes()
- # Choose a random angle from which to attach the turtle label.
- if turtles.sprite_list is not None:
- self.spr = Sprite(turtles.sprite_list, 0, 0, self.shapes[0])
+ # Choose a random angle from which to attach the turtle
+ # label to be used when sharing.
angle = uniform(0, pi * 4 / 3.0) # 240 degrees
- w = self.shapes[0].get_width()
- r = w * 0.67
- # Restrict angle the the sides 30-150; 210-330
+ width = self._shapes[0].get_width()
+ radius = width * 0.67
+ # Restrict the angle to the sides: 30-150; 210-330
if angle > pi * 2 / 3.0:
angle += pi / 2.0 # + 90
- self.label_xy = [int(r * sin(angle)),
- int(r * cos(angle) + w / 2.0)]
+ self.label_xy = [int(radius * sin(angle)),
+ int(radius * cos(angle) + width / 2.0)]
else:
angle += pi / 6.0 # + 30
- self.label_xy = [int(r * sin(angle) + w / 2.0),
- int(r * cos(angle) + w / 2.0)]
- else:
- self.spr = None
- turtles.add_to_dict(key, self)
+ self.label_xy = [int(radius * sin(angle) + width / 2.0),
+ int(radius * cos(angle) + width / 2.0)]
+
+ self._turtles.add_to_dict(turtle_name, self)
+
+ def _calculate_sizes(self):
+ self._half_width = int(self.spr.rect.width / 2.0)
+ self._half_height = int(self.spr.rect.height / 2.0)
+ self._drag_radius = ((self._half_width * self._half_width) +
+ (self._half_height * self._half_height)) / 6
+
+ def set_remote(self):
+ self._remote = True
+
+ def get_remote(self):
+ return self._remote
def _prep_shapes(self, name, turtles=None, turtle_colors=None):
# If the turtle name is an int, we'll use a palette color as the
@@ -158,38 +255,38 @@ class Turtle:
if turtle_colors is not None:
self.colors = turtle_colors[:]
- self.shapes = generate_turtle_pixbufs(self.colors)
+ self._shapes = generate_turtle_pixbufs(self.colors)
elif use_color_table:
fill = wrap100(int_key)
stroke = wrap100(fill + 10)
self.colors = ['#%06x' % (COLOR_TABLE[fill]),
'#%06x' % (COLOR_TABLE[stroke])]
- self.shapes = generate_turtle_pixbufs(self.colors)
+ self._shapes = generate_turtle_pixbufs(self.colors)
else:
if turtles is not None:
self.colors = DEFAULT_TURTLE_COLORS
- self.shapes = turtles.get_pixbufs()
+ self._shapes = turtles.get_pixbufs()
def set_turtle_colors(self, turtle_colors):
''' reset the colors of a preloaded turtle '''
if turtle_colors is not None:
self.colors = turtle_colors[:]
- self.shapes = generate_turtle_pixbufs(self.colors)
- self.set_heading(self.heading)
+ self._shapes = generate_turtle_pixbufs(self.colors)
+ self.set_heading(self._heading, share=False)
def set_shapes(self, shapes, i=0):
- """ Reskin the turtle """
+ ''' Reskin the turtle '''
n = len(shapes)
if n == 1 and i > 0: # set shape[i]
- if i < len(self.shapes):
- self.shapes[i] = shapes[0]
+ if i < len(self._shapes):
+ self._shapes[i] = shapes[0]
elif n == SHAPES: # all shapes have been precomputed
- self.shapes = shapes[:]
+ self._shapes = shapes[:]
else: # rotate shapes
if n != 1:
debug_output("%d images passed to set_shapes: ignoring" % (n),
- self.tw.running_sugar)
- if self.heading == 0.0: # rotate the shapes
+ self._turtles.turtle_window.running_sugar)
+ if self._heading == 0.0: # rotate the shapes
images = []
w, h = shapes[0].get_width(), shapes[0].get_height()
nw = nh = int(sqrt(w * w + h * h))
@@ -197,113 +294,447 @@ class Turtle:
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, nw, nh)
context = cairo.Context(surface)
context = gtk.gdk.CairoContext(context)
- context.translate(nw / 2., nh / 2.)
+ context.translate(nw / 2.0, nh / 2.0)
context.rotate(i * 10 * pi / 180.)
- context.translate(-nw / 2., -nh / 2.)
- context.set_source_pixbuf(shapes[0], (nw - w) / 2.,
- (nh - h) / 2.)
+ context.translate(-nw / 2.0, -nh / 2.0)
+ context.set_source_pixbuf(shapes[0], (nw - w) / 2.0,
+ (nh - h) / 2.0)
context.rectangle(0, 0, nw, nh)
context.fill()
images.append(surface)
- self.shapes = images[:]
+ self._shapes = images[:]
else: # associate shape with image at current heading
- j = int(self.heading + 5) % 360 / (360 / SHAPES)
- self.shapes[j] = shapes[0]
- self.custom_shapes = True
+ j = int(self._heading + 5) % 360 / (360 / SHAPES)
+ self._shapes[j] = shapes[0]
+ self._custom_shapes = True
self.show()
+ self._calculate_sizes()
def reset_shapes(self):
- """ Reset the shapes to the standard turtle """
- if self.custom_shapes:
- self.shapes = generate_turtle_pixbufs(self.colors)
- self.custom_shapes = False
-
- def set_heading(self, heading):
- """ Set the turtle heading (one shape per 360/SHAPES degrees) """
- self.heading = heading
- i = (int(self.heading + 5) % 360) / (360 / SHAPES)
- if not self.hidden and self.spr is not None:
+ ''' Reset the shapes to the standard turtle '''
+ if self._custom_shapes:
+ self._shapes = generate_turtle_pixbufs(self.colors)
+ self._custom_shapes = False
+ self._calculate_sizes()
+
+ def set_heading(self, heading, share=True):
+ ''' Set the turtle heading (one shape per 360/SHAPES degrees) '''
+ try:
+ self._heading = heading
+ except (TypeError, ValueError):
+ debug_output('bad value sent to %s' % (__name__),
+ self._turtles.turtle_window.running_sugar)
+ return
+ self._heading %= 360
+
+ i = (int(self._heading + 5) % 360) / (360 / SHAPES)
+ if not self._hidden and self.spr is not None:
try:
- self.spr.set_shape(self.shapes[i])
+ self.spr.set_shape(self._shapes[i])
except IndexError:
- self.spr.set_shape(self.shapes[0])
-
- def set_color(self, color):
- """ Set the pen color for this turtle. """
- self.pen_color = color
-
- def set_gray(self, gray):
- """ Set the pen gray level for this turtle. """
- self.pen_gray = gray
-
- def set_shade(self, shade):
- """ Set the pen shade for this turtle. """
- self.pen_shade = shade
-
- def set_pen_size(self, pen_size):
- """ Set the pen size for this turtle. """
- self.pen_size = pen_size
+ self.spr.set_shape(self._shapes[0])
+
+ if self._turtles.turtle_window.sharing() and share:
+ event = 'r|%s' % (data_to_string([self._turtles.turtle_window.nick,
+ round_int(self._heading)]))
+ self._turtles.turtle_window.send_event(event)
+
+ def set_color(self, color=None, share=True):
+ ''' Set the pen color for this turtle. '''
+ # Special case for color blocks
+ if color is not None and color in COLORDICT:
+ self.set_shade(COLORDICT[color][1], share)
+ self.set_gray(COLORDICT[color][2], share)
+ if COLORDICT[color][0] is not None:
+ self.set_color(COLORDICT[color][0], share)
+ color = COLORDICT[color][0]
+ else:
+ color = self._pen_color
+ elif color is None:
+ color = self._pen_color
- def set_pen_state(self, pen_state):
- """ Set the pen state (down==True) for this turtle. """
- self.pen_state = pen_state
+ try:
+ self._pen_color = color
+ except (TypeError, ValueError):
+ debug_output('bad value sent to %s' % (__name__),
+ self._turtles.turtle_window.running_sugar)
+ return
+
+ self._turtles.turtle_window.canvas.set_fgcolor(shade=self._pen_shade,
+ gray=self._pen_gray,
+ color=self._pen_color)
+
+ if self._turtles.turtle_window.sharing() and share:
+ event = 'c|%s' % (data_to_string([self._turtles.turtle_window.nick,
+ round_int(self._pen_color)]))
+ self._turtles.turtle_window.send_event(event)
+
+ def set_gray(self, gray=None, share=True):
+ ''' Set the pen gray level for this turtle. '''
+ if gray is not None:
+ try:
+ self._pen_gray = gray
+ except (TypeError, ValueError):
+ debug_output('bad value sent to %s' % (__name__),
+ self._turtles.turtle_window.running_sugar)
+ return
+
+ if self._pen_gray < 0:
+ self._pen_gray = 0
+ if self._pen_gray > 100:
+ self._pen_gray = 100
+
+ self._turtles.turtle_window.canvas.set_fgcolor(shade=self._pen_shade,
+ gray=self._pen_gray,
+ color=self._pen_color)
+
+ if self._turtles.turtle_window.sharing() and share:
+ event = 'g|%s' % (data_to_string([self._turtles.turtle_window.nick,
+ round_int(self._pen_gray)]))
+ self._turtles.turtle_window.send_event(event)
+
+ def set_shade(self, shade=None, share=True):
+ ''' Set the pen shade for this turtle. '''
+ if shade is not None:
+ try:
+ self._pen_shade = shade
+ except (TypeError, ValueError):
+ debug_output('bad value sent to %s' % (__name__),
+ self._turtles.turtle_window.running_sugar)
+ return
+
+ self._turtles.turtle_window.canvas.set_fgcolor(shade=self._pen_shade,
+ gray=self._pen_gray,
+ color=self._pen_color)
+
+ if self._turtles.turtle_window.sharing() and share:
+ event = 's|%s' % (data_to_string([self._turtles.turtle_window.nick,
+ round_int(self._pen_shade)]))
+ self._turtles.turtle_window.send_event(event)
+
+ def set_pen_size(self, pen_size=None, share=True):
+ ''' Set the pen size for this turtle. '''
+ if pen_size is not None:
+ try:
+ self._pen_size = max(0, pen_size)
+ except (TypeError, ValueError):
+ debug_output('bad value sent to %s' % (__name__),
+ self._turtles.turtle_window.running_sugar)
+ return
+
+ self._turtles.turtle_window.canvas.set_pen_size(self._pen_size)
+
+ if self._turtles.turtle_window.sharing() and share:
+ event = 'w|%s' % (data_to_string([self._turtles.turtle_window.nick,
+ round_int(self._pen_size)]))
+ self._turtles.turtle_window.send_event(event)
+
+ def set_pen_state(self, pen_state=None, share=True):
+ ''' Set the pen state (down==True) for this turtle. '''
+ if pen_state is not None:
+ self._pen_state = pen_state
+
+ if self._turtles.turtle_window.sharing() and share:
+ event = 'p|%s' % (data_to_string([self._turtles.turtle_window.nick,
+ self._pen_state]))
+ self._turtles.turtle_window.send_event(event)
+
+ def set_fill(self, state=False):
+ self._pen_fill = state
+ if not self._pen_fill:
+ self._poly_points = []
+
+ def set_poly_points(self, poly_points=None):
+ if poly_points is not None:
+ self._poly_points = poly_points[:]
+
+ def start_fill(self):
+ self._pen_fill = True
+ self._poly_points = []
+
+ def stop_fill(self, share=True):
+ self._pen_fill = False
+ if len(self._poly_points) == 0:
+ return
+
+ self._turtles.turtle_window.canvas.fill_polygon(self._poly_points)
+
+ if self._turtles.turtle_window.sharing() and share:
+ shared_poly_points = []
+ for p in self._poly_points:
+ shared_poly_points.append(
+ (self._turtles.screen_to_turtle_coordinates(p)))
+ event = 'F|%s' % (data_to_string(
+ [self._turtles.turtle_window.nick,
+ shared_poly_points]))
+ self._turtles.turtle_window.send_event(event)
+ self._poly_points = []
def hide(self):
- """ Hide the turtle. """
if self.spr is not None:
self.spr.hide()
if self.label_block is not None:
self.label_block.spr.hide()
- self.hidden = True
+ self._hidden = True
def show(self):
- """ Show the turtle. """
if self.spr is not None:
self.spr.set_layer(TURTLE_LAYER)
- self.hidden = False
- self.move((self.x, self.y))
- self.set_heading(self.heading)
+ self._hidden = False
+ self.move_turtle_spr((self._x, self._y))
+ self.set_heading(self._heading)
if self.label_block is not None:
self.label_block.spr.set_layer(TURTLE_LAYER + 1)
- def move(self, pos):
- """ Move the turtle. """
- self.x, self.y = pos[0], pos[1]
- if not self.hidden and self.spr is not None:
- self.spr.move((int(pos[0]), int(pos[1])))
+ def move_turtle(self, pos=None):
+ ''' Move the turtle's position '''
+ if pos is None:
+ pos = self.get_xy()
+
+ self._x, self._y = pos[0], pos[1]
+ if self.spr is not None:
+ self.move_turtle_spr(pos)
+
+ def move_turtle_spr(self, pos):
+ ''' Move the turtle's sprite '''
+ pos = self._turtles.turtle_to_screen_coordinates(pos)
+
+ pos[0] -= self._half_width
+ pos[1] -= self._half_height
+
+ if not self._hidden and self.spr is not None:
+ self.spr.move(pos)
if self.label_block is not None:
- self.label_block.spr.move((int(pos[0] + self.label_xy[0]),
- int(pos[1] + self.label_xy[1])))
- return(self.x, self.y)
+ self.label_block.spr.move((pos[0] + self.label_xy[0],
+ pos[1] + self.label_xy[1]))
+
+ def right(self, degrees, share=True):
+ ''' Rotate turtle clockwise '''
+ try:
+ self._heading += degrees
+ except (TypeError, ValueError):
+ debug_output('bad value sent to %s' % (__name__),
+ self._turtles.turtle_window.running_sugar)
+ return
+ self._heading %= 360
+
+ if self._turtles.turtle_window.sharing() and share:
+ event = 'r|%s' % (data_to_string([self._turtles.turtle_window.nick,
+ round_int(self._heading)]))
+ self._turtles.turtle_window.send_event(event)
+
+ def _draw_line(self, old, new, pendown):
+ if self._pen_state and pendown:
+ self._turtles.turtle_window.canvas.set_source_rgb()
+ pos1 = self._turtles.turtle_to_screen_coordinates(old)
+ pos2 = self._turtles.turtle_to_screen_coordinates(new)
+ self._turtles.turtle_window.canvas.draw_line(pos1[0], pos1[1],
+ pos2[0], pos2[1])
+ if self._pen_fill:
+ if self._poly_points == []:
+ self._poly_points.append(('move', pos1[0], pos1[1]))
+ self._poly_points.append(('line', pos2[0], pos2[1]))
+
+ def forward(self, distance, share=True):
+ scaled_distance = distance * self._turtles.turtle_window.coord_scale
+
+ old = self.get_xy()
+ try:
+ xcor = old[0] + scaled_distance * sin(self._heading * DEGTOR)
+ ycor = old[1] + scaled_distance * cos(self._heading * DEGTOR)
+ except (TypeError, ValueError):
+ debug_output('bad value sent to %s' % (__name__),
+ self._turtles.turtle_window.running_sugar)
+ return
+
+ self._draw_line(old, (xcor, ycor), True)
+ self.move_turtle((xcor, ycor))
+
+ if self._turtles.turtle_window.sharing() and share:
+ event = 'f|%s' % (data_to_string([self._turtles.turtle_window.nick,
+ int(distance)]))
+ self._turtles.turtle_window.send_event(event)
+
+ def set_xy(self, pos, share=True, pendown=True):
+ old = self.get_xy()
+
+ try:
+ xcor = pos[0] * self._turtles.turtle_window.coord_scale
+ ycor = pos[1] * self._turtles.turtle_window.coord_scale
+ except (TypeError, ValueError):
+ debug_output('bad value sent to %s' % (__name__),
+ self._turtles.turtle_window.running_sugar)
+ return
+
+ self._draw_line(old, (xcor, ycor), pendown)
+ self.move_turtle((xcor, ycor))
+
+ if self._turtles.turtle_window.sharing() and share:
+ event = 'x|%s' % (data_to_string([self._turtles.turtle_window.nick,
+ [round_int(xcor),
+ round_int(ycor)]]))
+ self._turtles.turtle_window.send_event(event)
+
+ def arc(self, a, r, share=True):
+ ''' Draw an arc '''
+ if self._pen_state:
+ self._turtles.turtle_window.canvas.set_source_rgb()
+ try:
+ if a < 0:
+ pos = self.larc(-a, r)
+ else:
+ pos = self.rarc(a, r)
+ except (TypeError, ValueError):
+ debug_output('bad value sent to %s' % (__name__),
+ self._turtles.turtle_window.running_sugar)
+ return
+
+ self.move_turtle(pos)
+
+ if self._turtles.turtle_window.sharing() and share:
+ event = 'a|%s' % (data_to_string([self._turtles.turtle_window.nick,
+ [round_int(a), round_int(r)]]))
+ self._turtles.turtle_window.send_event(event)
+
+ def rarc(self, a, r):
+ ''' draw a clockwise arc '''
+ r *= self._turtles.turtle_window.coord_scale
+ if r < 0:
+ r = -r
+ a = -a
+ pos = self.get_xy()
+ cx = pos[0] + r * cos(self._heading * DEGTOR)
+ cy = pos[1] - r * sin(self._heading * DEGTOR)
+ if self._pen_state:
+ npos = self._turtles.turtle_to_screen_coordinates((cx, cy))
+ self._turtles.turtle_window.canvas.rarc(npos[0], npos[1], r, a,
+ self._heading)
+
+ if self._pen_fill:
+ if self._poly_points == []:
+ self._poly_points.append(('move', npos[0], npos[1]))
+ self._poly_points.append(('rarc', npos[0], npos[1], r,
+ (self._heading - 180) * DEGTOR,
+ (self._heading - 180 + a)
+ * DEGTOR))
+
+ self.right(a, False)
+ return [cx - r * cos(self._heading * DEGTOR),
+ cy + r * sin(self._heading * DEGTOR)]
+
+ def larc(self, a, r):
+ ''' draw a counter-clockwise arc '''
+ r *= self._turtles.turtle_window.coord_scale
+ if r < 0:
+ r = -r
+ a = -a
+ pos = self.get_xy()
+ cx = pos[0] - r * cos(self._heading * DEGTOR)
+ cy = pos[1] + r * sin(self._heading * DEGTOR)
+ if self._pen_state:
+ npos = self._turtles.turtle_to_screen_coordinates((cx, cy))
+ self._turtles.turtle_window.canvas.larc(npos[0], npos[1], r, a,
+ self._heading)
+
+ if self._pen_fill:
+ if self._poly_points == []:
+ self._poly_points.append(('move', npos[0], npos[1]))
+ self._poly_points.append(('larc', npos[0], npos[1], r,
+ (self._heading) * DEGTOR,
+ (self._heading - a) * DEGTOR))
+
+ self.right(-a, False)
+ return [cx + r * cos(self._heading * DEGTOR),
+ cy - r * sin(self._heading * DEGTOR)]
+
+ def draw_pixbuf(self, pixbuf, a, b, x, y, w, h, path, share=True):
+ ''' Draw a pixbuf '''
+
+ self._turtles.turtle_window.canvas.draw_pixbuf(
+ pixbuf, a, b, x, y, w, h, self._heading)
+
+ if self._turtles.turtle_window.sharing() and share:
+ if self._turtles.turtle_window.running_sugar:
+ tmp_path = get_path(self._turtles.turtle_window.activity,
+ 'instance')
+ else:
+ tmp_path = '/tmp'
+ tmp_file = os.path.join(
+ get_path(self._turtles.turtle_window.activity, 'instance'),
+ 'tmpfile.png')
+ pixbuf.save(tmp_file, 'png', {'quality': '100'})
+ data = image_to_base64(tmp_file, tmp_path)
+ height = pixbuf.get_height()
+ width = pixbuf.get_width()
+
+ pos = self._turtles.screen_to_turtle_coordinates((x, y))
+
+ event = 'P|%s' % (data_to_string([self._turtles.turtle_window.nick,
+ [round_int(a), round_int(b),
+ round_int(pos[0]),
+ round_int(pos[1]),
+ round_int(w), round_int(h),
+ round_int(width),
+ round_int(height),
+ data]]))
+ gobject.idle_add(self._turtles.turtle_window.send_event, event)
+
+ os.remove(tmp_file)
+
+ def draw_text(self, label, x, y, size, w, share=True):
+ ''' Draw text '''
+ self._turtles.turtle_window.canvas.draw_text(
+ label, x, y, size, w, self._heading,
+ self._turtles.turtle_window.coord_scale)
+
+ if self._turtles.turtle_window.sharing() and share:
+ event = 'W|%s' % (data_to_string([self._turtles.turtle_window.nick,
+ [label, round_int(x),
+ round_int(y), round_int(size),
+ round_int(w)]]))
+ self._turtles.turtle_window.send_event(event)
def get_name(self):
- ''' return turtle name (key) '''
- return self.name
+ return self._name
def get_xy(self):
- """ Return the turtle's x, y coordinates. """
- return(self.x, self.y)
+ return [self._x, self._y]
+
+ def get_x(self):
+ return self._x
+
+ def get_y(self):
+ return self._y
def get_heading(self):
- """ Return the turtle's heading. """
- return(self.heading)
+ return self._heading
def get_color(self):
- """ Return the turtle's color. """
- return(self.pen_color)
+ return self._pen_color
def get_gray(self):
- """ Return the turtle's gray level. """
- return(self.pen_gray)
+ return self._pen_gray
def get_shade(self):
- """ Return the turtle's shade. """
- return(self.pen_shade)
+ return self._pen_shade
def get_pen_size(self):
- """ Return the turtle's pen size. """
- return(self.pen_size)
+ return self._pen_size
def get_pen_state(self):
- """ Return the turtle's pen state. """
- return(self.pen_state)
+ return self._pen_state
+
+ def get_fill(self):
+ return self._pen_fill
+
+ def get_poly_points(self):
+ return self._poly_points
+
+ def get_pixel(self):
+ pos = self._turtles.turtle_to_screen_coordinates(self.get_xy())
+ return self._turtles.turtle_window.canvas.get_pixel(pos[0], pos[1])
+
+ def get_drag_radius(self):
+ if self._drag_radius is None:
+ self._calculate_sizes()
+ return self._drag_radius
diff --git a/TurtleArt/tawindow.py b/TurtleArt/tawindow.py
index 731260f..97c0124 100644
--- a/TurtleArt/tawindow.py
+++ b/TurtleArt/tawindow.py
@@ -101,11 +101,12 @@ class TurtleArtWindow():
def __init__(self, canvas_window, path, parent=None, activity=None,
mycolors=None, mynick=None, turtle_canvas=None,
- running_sugar=True):
- """parent -- the GTK Window that TA runs in
- activity -- the object that instantiated this TurtleArtWindow (in
+ running_sugar=True, running_turtleart=True):
+ '''parent -- the GTK Window that TA runs in
+ activity -- the object that instantiated this TurtleArtWindow (in
GNOME, a TurtleMain instance)
- """
+ running_turtleart -- are we running TA or exported python code?
+ '''
self.parent = parent
self.turtle_canvas = turtle_canvas
self._loaded_project = ''
@@ -115,6 +116,7 @@ class TurtleArtWindow():
self.gst_available = _GST_AVAILABLE
self.running_sugar = False
self.nick = None
+ self.running_turtleart = running_turtleart
if isinstance(canvas_window, gtk.DrawingArea):
self.interactive_mode = True
self.window = canvas_window
@@ -145,6 +147,7 @@ class TurtleArtWindow():
else:
self.activity = parent
+ # loading and saving
self.path = path
self.load_save_folder = os.path.join(path, 'samples')
self.py_load_save_folder = os.path.join(path, 'pysamples')
@@ -152,6 +155,8 @@ class TurtleArtWindow():
self.used_block_list = [] # Which blocks has the user used?
self.save_folder = None
self.save_file_name = None
+
+ # dimensions
self.width = gtk.gdk.screen_width()
self.height = gtk.gdk.screen_height()
self.rect = gtk.gdk.Rectangle(0, 0, 0, 0)
@@ -174,6 +179,7 @@ class TurtleArtWindow():
self.sharing_blocks = False
self.deleting_blocks = False
+ # find out which character to use as decimal point
try:
locale.setlocale(locale.LC_NUMERIC, '')
except locale.Error:
@@ -182,8 +188,8 @@ class TurtleArtWindow():
if self.decimal_point == '' or self.decimal_point is None:
self.decimal_point = '.'
+ # settings that depend on the hardware
self.orientation = HORIZONTAL_PALETTE
-
self.hw = get_hardware()
self.lead = 1.0
if self.hw in (XO1, XO15, XO175, XO4):
@@ -208,8 +214,9 @@ class TurtleArtWindow():
self.nop = 'nop'
self.loaded = 0
self.step_time = 0
- self.hide = False
- self.palette = True
+ # show/ hide palettes depending on whether we're running in TA or not
+ self.hide = not self.running_turtleart
+ self.palette = self.running_turtleart
self.coord_scale = 1
self.buddies = []
self._saved_string = ''
@@ -247,6 +254,7 @@ class TurtleArtWindow():
self.turtle_movement_to_share = None
self.paste_offset = 20 # Don't paste on top of where you copied.
+ # common properties of all blocks (font size, decimal point, ...)
self.block_list = Blocks(font_scale_factor=self.scale,
decimal_point=self.decimal_point)
if self.interactive_mode:
@@ -254,23 +262,24 @@ class TurtleArtWindow():
else:
self.sprite_list = None
+ # canvas object that supports the basic drawing functionality
self.canvas = TurtleGraphics(self, self.width, self.height)
if self.hw == XO175 and self.canvas.width == 1024:
self.hw = XO30
if self.interactive_mode:
self.sprite_list.set_cairo_context(self.canvas.canvas)
- self.turtles = Turtles(self.sprite_list)
- if self.nick is None:
- self.default_turtle_name = DEFAULT_TURTLE
- else:
- self.default_turtle_name = self.nick
+ self.turtles = Turtles(self)
+ if self.nick is not None:
+ self.turtles.set_default_turtle_name(self.nick)
if mycolors is None:
- Turtle(self.turtles, self.default_turtle_name)
+ Turtle(self.turtles, self.turtles.get_default_turtle_name())
else:
- Turtle(self.turtles, self.default_turtle_name, mycolors.split(','))
- self.active_turtle = self.turtles.get_turtle(self.default_turtle_name)
- self.active_turtle.show()
+ Turtle(self.turtles, self.turtles.get_default_turtle_name(),
+ mycolors.split(','))
+ self.turtles.set_active_turtle(
+ self.turtles.get_turtle(self.turtles.get_default_turtle_name()))
+ self.turtles.get_active_turtle().show()
self.canvas.clearscreen(False)
@@ -284,8 +293,10 @@ class TurtleArtWindow():
self.saved_pictures = []
self.block_operation = ''
- from tabasics import Palettes
- self._basic_palettes = Palettes(self)
+ # only in TA: setup basic palettes
+ if self.running_turtleart:
+ from tabasics import Palettes
+ self._basic_palettes = Palettes(self)
if self.interactive_mode:
gobject.idle_add(self._lazy_init)
@@ -293,16 +304,12 @@ class TurtleArtWindow():
self._init_plugins()
self._setup_plugins()
- def _lazy_init(self, init_palettes=True, load_toolbar_shapes=True):
- """init_palettes -- whether to initialize the palettes (should always
- be True in TA)
- load_toolbar_shapes -- passed on to _setup_misc()
- """
+ def _lazy_init(self):
self._init_plugins()
self._setup_plugins()
- self._setup_misc(load_toolbar_shapes=load_toolbar_shapes)
+ self._setup_misc()
- if init_palettes:
+ if self.running_turtleart:
self._basic_palettes.make_trash_palette()
for name in palette_init_on_start:
debug_output('initing palette %s' % (name), self.running_sugar)
@@ -347,7 +354,7 @@ class TurtleArtWindow():
pname = os.path.join(path, dirname, dirname + '.py')
if os.path.exists(pname):
plugin_files.append(dirname)
- return plugin_files
+ return plugin_files
def _init_plugins(self):
''' Try importing plugin files from the plugin dir. '''
@@ -502,11 +509,8 @@ class TurtleArtWindow():
os.path.join(self.path, path, filename + '.svg')))
break
- def _setup_misc(self, load_toolbar_shapes=True):
- ''' Misc. sprites for status, overlays, etc.
- load_toolbar_shapes -- whether to load the toolbar shapes/ icons if
- we're not in sugar (should always be True in TA)
- '''
+ def _setup_misc(self):
+ ''' Misc. sprites for status, overlays, etc. '''
self.load_media_shapes()
for i, name in enumerate(STATUS_SHAPES):
# Temporary hack to use wider shapes
@@ -534,7 +538,7 @@ class TurtleArtWindow():
self.overlay_shapes[name].hide()
self.overlay_shapes[name].type = 'overlay'
- if load_toolbar_shapes and not self.running_sugar:
+ if self.running_turtleart and not self.running_sugar:
# offset = 2 * self.width - 55 * len(TOOLBAR_SHAPES)
offset = 55 * (1 + len(palette_blocks))
for i, name in enumerate(TOOLBAR_SHAPES):
@@ -705,15 +709,18 @@ class TurtleArtWindow():
def draw_overlay(self, overlay):
''' Draw a coordinate grid onto the canvas. '''
- save_heading = self.canvas.heading
- self.canvas.heading = 0
- w = self.overlay_shapes[overlay].rect[2]
- h = self.overlay_shapes[overlay].rect[3]
+ width = self.overlay_shapes[overlay].rect[2]
+ height = self.overlay_shapes[overlay].rect[3]
+ if self.running_sugar:
+ y_offset = 0
+ else:
+ y_offset = ICON_SIZE
self.canvas.draw_surface(
self.overlay_shapes[overlay].cached_surfaces[0],
- (self.canvas.width - w) / 2.,
- (self.canvas.height - h) / 2., w, h)
- self.canvas.heading = save_heading
+ (self.canvas.width - width) / 2.0,
+ (self.canvas.height - height + y_offset) / 2.0,
+ width,
+ height)
def update_overlay_position(self, widget, event):
''' Reposition the overlays when window size changes '''
@@ -743,7 +750,7 @@ class TurtleArtWindow():
self.metric = False
self.canvas.width = self.width
self.canvas.height = self.height
- self.canvas.move_turtle()
+ self.turtles.get_active_turtle().move_turtle()
def hideshow_button(self):
''' Hide/show button '''
@@ -1710,13 +1717,13 @@ before making changes to your Turtle Blocks program'))
def _look_for_a_turtle(self, spr, x, y):
# Next, look for a turtle
- t = self.turtles.spr_to_turtle(spr)
- if t is not None:
+ turtle = self.turtles.spr_to_turtle(spr)
+ if turtle is not None:
# If turtle is shared, ignore click
- if self.remote_turtle(t.get_name()):
+ if self.remote_turtle(turtle.get_name()):
return True
- self.selected_turtle = t
- self.canvas.set_turtle(self.turtles.get_turtle_key(t))
+ self.selected_turtle = turtle
+ self.turtles.set_turtle(self.turtles.get_turtle_key(turtle))
self._turtle_pressed(x, y)
self.update_counter = 0
return True
@@ -2429,20 +2436,22 @@ before making changes to your Turtle Blocks program'))
self._adjust_dock_positions(c)
def _turtle_pressed(self, x, y):
- (tx, ty) = self.selected_turtle.get_xy()
- w = self.selected_turtle.spr.rect.width / 2
- h = self.selected_turtle.spr.rect.height / 2
- dx = x - tx - w
- dy = y - ty - h
- # if x, y is near the edge, rotate
+ pos = self.selected_turtle.get_xy()
+ tpos = self.turtles.turtle_to_screen_coordinates(pos)
+ dx = x - tpos[0]
+ dy = y - tpos[1]
if not hasattr(self.lc, 'value_blocks'):
self.lc.find_value_blocks()
self.lc.update_values = True
- if (dx * dx) + (dy * dy) > ((w * w) + (h * h)) / 6:
- self.drag_turtle = \
- ('turn', self.canvas.heading - atan2(dy, dx) / DEGTOR, 0)
+ # Compare distance squared of drag position to sprite radius.
+ # If x, y is near the edge, rotate.
+ if (dx * dx) + (dy * dy) > self.selected_turtle.get_drag_radius():
+ self.drag_turtle = (
+ 'turn',
+ self.selected_turtle.get_heading() - atan2(dy, dx) / DEGTOR,
+ 0)
else:
- self.drag_turtle = ('move', x - tx, y - ty)
+ self.drag_turtle = ('move', x - tpos[0], y - tpos[1])
def _move_cb(self, win, event):
x, y = xy(event)
@@ -2455,18 +2464,18 @@ before making changes to your Turtle Blocks program'))
''' Share turtle movement and rotation after button up '''
if self.sharing():
nick = self.turtle_movement_to_share.get_name()
- self.send_event("r|%s" %
- (data_to_string([nick,
- round_int(self.canvas.heading)])))
- if self.canvas.pendown:
+ self.send_event("r|%s" % (data_to_string(
+ [nick,
+ round_int(self.turtles.get_active_turtle().get_heading())])))
+ if self.turtles.get_active_turtle().get_pen_state():
self.send_event('p|%s' % (data_to_string([nick, False])))
put_pen_back_down = True
else:
put_pen_back_down = False
- self.send_event("x|%s" %
- (data_to_string([nick,
- [round_int(self.canvas.xcor),
- round_int(self.canvas.ycor)]])))
+ self.send_event("x|%s" % (data_to_string(
+ [nick,
+ [round_int(self.turtles.get_active_turtle().get_xy()[0]),
+ round_int(self.turtles.get_active_turtle().get_xy()[1])]])))
if put_pen_back_down:
self.send_event('p|%s' % (data_to_string([nick, True])))
self.turtle_movement_to_share = None
@@ -2493,34 +2502,37 @@ before making changes to your Turtle Blocks program'))
# First, check to see if we are dragging or rotating a turtle.
if self.selected_turtle is not None:
- dtype, dragx, dragy = self.drag_turtle
- (sx, sy) = self.selected_turtle.get_xy()
- # self.canvas.set_turtle(self.selected_turtle.get_name())
+ drag_type, dragx, dragy = self.drag_turtle
self.update_counter += 1
- if dtype == 'move':
- dx = x - dragx - sx + self.selected_turtle.spr.rect.width / 2
- dy = y - dragy - sy + self.selected_turtle.spr.rect.height / 2
+ if drag_type == 'move':
+ dx = x - dragx
+ dy = y - dragy
self.selected_turtle.spr.set_layer(TOP_LAYER)
- tx, ty = self.canvas.screen_to_turtle_coordinates(sx + dx,
- sy + dy)
- if self.canvas.pendown:
- self.canvas.setpen(False)
- self.canvas.setxy(tx, ty, share=False)
- self.canvas.setpen(True)
+ pos = self.turtles.screen_to_turtle_coordinates((dx, dy))
+ if self.selected_turtle.get_pen_state():
+ self.selected_turtle.set_pen_state(False)
+ self.selected_turtle.set_xy(pos, share=False)
+ self.selected_turtle.set_pen_state(True)
else:
- self.canvas.setxy(tx, ty, share=False)
+ self.selected_turtle.set_xy(pos, share=False)
if self.update_counter % 5:
self.lc.update_label_value(
- 'xcor', self.canvas.xcor / self.coord_scale)
+ 'xcor', self.selected_turtle.get_xy()[0] /
+ self.coord_scale)
self.lc.update_label_value(
- 'ycor', self.canvas.ycor / self.coord_scale)
+ 'ycor', self.selected_turtle.get_xy()[1] /
+ self.coord_scale)
else:
- dx = x - sx - self.selected_turtle.spr.rect.width / 2
- dy = y - sy - self.selected_turtle.spr.rect.height / 2
- self.canvas.seth(int(dragx + atan2(dy, dx) / DEGTOR + 5) /
- 10 * 10, share=False)
+ spos = self.turtles.turtle_to_screen_coordinates(
+ self.selected_turtle.get_xy())
+ dx = x - spos[0]
+ dy = y - spos[1]
+ self.turtles.get_active_turtle().set_heading(
+ int(dragx + atan2(dy, dx) / DEGTOR + 5) / 10 * 10,
+ share=False)
if self.update_counter % 5:
- self.lc.update_label_value('heading', self.canvas.heading)
+ self.lc.update_label_value(
+ 'heading', self.selected_turtle.get_heading())
if self.update_counter % 20:
self.display_coordinates()
self.turtle_movement_to_share = self.selected_turtle
@@ -2677,30 +2689,26 @@ before making changes to your Turtle Blocks program'))
# We may have been moving the turtle
if self.selected_turtle is not None:
- (tx, ty) = self.selected_turtle.get_xy()
- k = self.turtles.get_turtle_key(self.selected_turtle)
-
+ pos = self.selected_turtle.get_xy()
+ spos = self.turtles.turtle_to_screen_coordinates(pos)
+ turtle_name = self.turtles.get_turtle_key(self.selected_turtle)
# Remove turtles by dragging them onto the trash palette.
- if self._in_the_trash(tx, ty):
+ if self._in_the_trash(spos[0], spos[1]):
# If it is the default turtle, just recenter it.
- if k == self.default_turtle_name:
+ if turtle_name == self.turtles.get_default_turtle_name():
self._move_turtle(0, 0)
- self.canvas.heading = 0
- self.canvas.turn_turtle()
- self.lc.update_label_value('heading', self.canvas.heading)
+ self.selected_turtle.set_heading(0)
+ self.lc.update_label_value('heading', 0)
else:
self.selected_turtle.hide()
- self.turtles.remove_from_dict(k)
- self.active_turtle = None
+ self.turtles.remove_from_dict(turtle_name)
+ self.turtles.set_active_turtle(None)
else:
- self._move_turtle(
- tx - self.canvas.width / 2. +
- self.active_turtle.spr.rect.width / 2.,
- self.canvas.height / 2. - ty -
- self.active_turtle.spr.rect.height / 2.)
+ self._move_turtle(pos[0], pos[1])
+
self.selected_turtle = None
- if self.active_turtle is None:
- self.canvas.set_turtle(self.default_turtle_name)
+ if self.turtles.get_active_turtle() is None:
+ self.turtles.set_turtle(self.turtles.get_default_turtle_name())
self.display_coordinates()
return
@@ -2776,21 +2784,23 @@ before making changes to your Turtle Blocks program'))
turtle.label_block.spr.set_label(name[0:4] + '…')
else:
turtle.label_block.spr.set_label(name)
+ turtle.set_remote()
turtle.show()
def _move_turtle(self, x, y):
''' Move the selected turtle to (x, y). '''
- self.canvas.xcor = x
- self.canvas.ycor = y
- self.canvas.move_turtle()
+ if self.drag_turtle[0] == 'move':
+ self.turtles.get_active_turtle().move_turtle((x, y))
if self.interactive_mode:
self.display_coordinates()
if self.running_sugar:
self.selected_turtle.spr.set_layer(TURTLE_LAYER)
- self.lc.update_label_value('xcor',
- self.canvas.xcor / self.coord_scale)
- self.lc.update_label_value('ycor',
- self.canvas.ycor / self.coord_scale)
+ self.lc.update_label_value(
+ 'xcor', self.turtles.get_active_turtle().get_xy()[0] /
+ self.coord_scale)
+ self.lc.update_label_value(
+ 'ycor', self.turtles.get_active_turtle().get_xy()[1] /
+ self.coord_scale)
def _click_block(self, x, y):
''' Click block: lots of special cases to handle... '''
@@ -3545,13 +3555,15 @@ before making changes to your Turtle Blocks program'))
def _jog_turtle(self, dx, dy):
''' Jog turtle '''
if dx == -1 and dy == -1:
- self.canvas.xcor = 0
- self.canvas.ycor = 0
+ x = 0
+ y = 0
else:
- self.canvas.xcor += dx
- self.canvas.ycor += dy
- self.active_turtle = self.turtles.spr_to_turtle(self.selected_spr)
- self.canvas.move_turtle()
+ pos = self.turtles.get_active_turtle().get_xy()
+ x = pos[0] + dx
+ y = pos[1] + dy
+ self.turtles.set_active_turtle(
+ self.turtles.spr_to_turtle(self.selected_spr))
+ self.turtles.get_active_turtle().move_turtle((x, y))
self.display_coordinates()
self.selected_turtle = None
@@ -3717,8 +3729,8 @@ before making changes to your Turtle Blocks program'))
if add_new_block:
# add a new block for this code at turtle position
- (tx, ty) = self.active_turtle.get_xy()
- self._new_block('userdefined', tx, ty)
+ pos = self.turtles.get_active_turtle().get_xy()
+ self._new_block('userdefined', pos[0], pos[1])
self.myblock[self.block_list.list.index(self.drag_group[0])] =\
self.python_code
self.set_userdefined(self.drag_group[0])
@@ -3764,7 +3776,6 @@ before making changes to your Turtle Blocks program'))
if self.running_sugar:
chooser_dialog(self.parent, 'org.laptop.Pippy',
self.load_python_code_from_journal)
- dsobject.destroy()
else:
self.load_python_code_from_file(fname=None, add_new_block=False)
@@ -3844,12 +3855,13 @@ before making changes to your Turtle Blocks program'))
def load_turtle(self, blk, key=1):
''' Restore a turtle from its saved state '''
tid, name, xcor, ycor, heading, color, shade, pensize = blk
- self.canvas.set_turtle(key)
- self.canvas.setxy(xcor, ycor, pendown=False)
- self.canvas.seth(heading)
- self.canvas.setcolor(color)
- self.canvas.setshade(shade)
- self.canvas.setpensize(pensize)
+ self.turtles.set_turtle(key)
+ self.turtles.get_active_turtle().set_xy(xcor, ycor, pendown=False)
+ self.turtles.get_active_turtle().set_heading(heading)
+ self.turtles.get_active_turtle().set_color(color)
+ self.turtles.get_active_turtle().set_shade(shade)
+ self.turtles.get_active_turtle().set_gray(100)
+ self.turtles.get_active_turtle().set_pen_size(pensize)
def load_block(self, b, offset=0):
''' Restore individual blocks from saved state '''
@@ -3952,8 +3964,8 @@ before making changes to your Turtle Blocks program'))
btype = OLD_NAMES[btype]
blk = Block(self.block_list, self.sprite_list, btype,
- b[2] + self.canvas.cx + offset,
- b[3] + self.canvas.cy + offset,
+ b[2] + offset,
+ b[3] + offset,
'block', values, self.block_scale)
# If it was an unknown block type, we need to match the number
@@ -4117,9 +4129,10 @@ before making changes to your Turtle Blocks program'))
def load_start(self, ta_file=None):
''' Start a new project with a 'start' brick '''
if ta_file is None:
- self.process_data([[0, "start", PALETTE_WIDTH + 20,
- self.toolbar_offset + PALETTE_HEIGHT + 20,
- [None, None]]])
+ self.process_data(
+ [[0, "start", PALETTE_WIDTH + 20,
+ self.toolbar_offset + PALETTE_HEIGHT + 20 + ICON_SIZE,
+ [None, None]]])
else:
self.process_data(data_from_file(ta_file))
@@ -4186,8 +4199,7 @@ before making changes to your Turtle Blocks program'))
if not save_project:
sx += 20
sy += 20
- data.append((blk.id, name, sx - self.canvas.cx,
- sy - self.canvas.cy, connections))
+ data.append((blk.id, name, sx, sy, connections))
if save_turtle:
for turtle in iter(self.turtles.dict):
# Don't save remote turtles
@@ -4195,12 +4207,15 @@ before making changes to your Turtle Blocks program'))
# Save default turtle as 'Yertle'
if turtle == self.nick:
turtle = DEFAULT_TURTLE
+ pos = self.turtles.get_active_turtle().get_xy()
data.append(
(-1,
['turtle', turtle],
- self.canvas.xcor, self.canvas.ycor,
- self.canvas.heading, self.canvas.color,
- self.canvas.shade, self.canvas.pensize))
+ pos[0], pos[1],
+ self.turtles.get_active_turtle().get_heading(),
+ self.turtles.get_active_turtle().get_color(),
+ self.turtles.get_active_turtle().get_shade(),
+ self.turtles.get_active_turtle().get_pen_size()))
return data
def display_coordinates(self, clear=False):
@@ -4212,9 +4227,11 @@ before making changes to your Turtle Blocks program'))
elif self.interactive_mode:
self.parent.set_title('')
else:
- x = round_int(float(self.canvas.xcor) / self.coord_scale)
- y = round_int(float(self.canvas.ycor) / self.coord_scale)
- h = round_int(self.canvas.heading)
+ x = round_int(float(self.turtles.get_active_turtle().get_xy()[0]) /
+ self.coord_scale)
+ y = round_int(float(self.turtles.get_active_turtle().get_xy()[1]) /
+ self.coord_scale)
+ h = round_int(self.turtles.get_active_turtle().get_heading())
if self.running_sugar:
if int(x) == x and int(y) == y and int(h) == h:
formatting = '(%d, %d) %d'
@@ -4595,7 +4612,7 @@ variable'))
raise logoerror("#emptybox")
def _prim_setbox(self, name, x, val):
- """ Define value of named box """
+ ''' Define value of named box '''
if x is not None:
if isinstance(convert(x, float, False), float):
if int(float(x)) == x:
diff --git a/activity/activity.info b/activity/activity.info
index ade868e..9d4ba87 100644
--- a/activity/activity.info
+++ b/activity/activity.info
@@ -1,6 +1,6 @@
[Activity]
name = TurtleBlocks
-activity_version = 182
+activity_version = 183
license = MIT
bundle_id = org.laptop.TurtleArtActivity
exec = sugar-activity TurtleArtActivity.TurtleArtActivity
diff --git a/plugins/turtle_blocks_extras/turtle_blocks_extras.py b/plugins/turtle_blocks_extras/turtle_blocks_extras.py
index fcbfafe..a272ec8 100644
--- a/plugins/turtle_blocks_extras/turtle_blocks_extras.py
+++ b/plugins/turtle_blocks_extras/turtle_blocks_extras.py
@@ -60,8 +60,8 @@ class Turtle_blocks_extras(Plugin):
""" a class for defining the extra palettes that distinguish Turtle Blocks
from Turtle Art """
- def __init__(self, parent):
- self.tw = parent
+ def __init__(self, turtle_window):
+ self.tw = turtle_window
def setup(self):
SKIN_PATHS.append('plugins/turtle_blocks_extras/images')
@@ -207,22 +207,6 @@ Journal'))
lambda self, x:
primitive_dictionary['show'](x, False))
- # deprecated
- primitive_dictionary['write'] = self._prim_write
- palette.add_block('write',
- hidden=True,
- colors=["#A0FF00", "#80A000"],
- style='basic-style-1arg',
- label=_('show'),
- default=[_('text'), 32],
- prim_name='write',
- logo_command='label',
- help_string=_('draws text or show media from the \
-Journal'))
- self.tw.lc.def_prim('write', 2,
- lambda self, x, y:
- primitive_dictionary['write'](x, y))
-
primitive_dictionary['setscale'] = self._prim_setscale
palette.add_block('setscale',
style='basic-style-1arg',
@@ -690,7 +674,7 @@ module found in the Journal'))
help_string=_('chooses which turtle to command'))
self.tw.lc.def_prim('turtle', 1,
lambda self, x:
- self.tw.canvas.set_turtle(x))
+ self.tw.turtles.set_turtle(x))
primitive_dictionary['activeturtle'] = self._prim_active_turtle
palette.add_block('activeturtle',
@@ -969,101 +953,6 @@ Journal objects'))
logo_command='bottomy')
self.tw.lc.def_prim('bottomy', 0, lambda self: CONSTANTS['bottomy'])
- # deprecated blocks
-
- primitive_dictionary['t1x1'] = self._prim_t1x1
- palette.add_block('template1x1',
- hidden=True,
- colors=["#0606FF", "#0606A0"],
- style='portfolio-style-1x1',
- label=' ',
- prim_name='t1x1',
- default=[_('Title'), 'None'],
- special_name=_('presentation 1x1'),
- string_or_number=True,
- help_string=_('presentation template: select \
-Journal object (with description)'))
- self.tw.lc.def_prim('t1x1', 2,
- lambda self, a, b:
- primitive_dictionary['t1x1'](a, b))
-
- primitive_dictionary['t1x1a'] = self._prim_t1x1a
- palette.add_block('template1x1a',
- hidden=True,
- colors=["#0606FF", "#0606A0"],
- style='portfolio-style-1x1',
- label=' ',
- prim_name='t1x1a',
- default=[_('Title'), 'None'],
- special_name=_('presentation 1x1'),
- string_or_number=True,
- help_string=_('presentation template: select \
-Journal object (no description)'))
- self.tw.lc.def_prim('t1x1a', 2,
- lambda self, a, b:
- primitive_dictionary['t1x1a'](a, b))
-
- primitive_dictionary['2x1'] = self._prim_t2x1
- palette.add_block('template2x1',
- hidden=True,
- colors=["#0606FF", "#0606A0"],
- style='portfolio-style-2x1',
- label=' ',
- prim_name='t2x1',
- default=[_('Title'), 'None', 'None'],
- special_name=_('presentation 2x1'),
- string_or_number=True,
- help_string=_("presentation template: select two \
-Journal objects"))
- self.tw.lc.def_prim('t2x1', 3,
- lambda self, a, b, c:
- primitive_dictionary['t2x1'](a, b, c))
-
- primitive_dictionary['1x2'] = self._prim_t1x2
- palette.add_block('template1x2',
- hidden=True,
- colors=["#0606FF", "#0606A0"],
- style='portfolio-style-1x2',
- label=' ',
- prim_name='t1x2',
- default=[_('Title'), 'None', 'None'],
- special_name=_('presentation 1x2'),
- string_or_number=True,
- help_string=_("presentation template: select two \
-Journal objects"))
- self.tw.lc.def_prim('t1x2', 3,
- lambda self, a, b, c:
- primitive_dictionary['t1x2'](a, b, c))
-
- primitive_dictionary['t2x2'] = self._prim_t2x2
- palette.add_block('template2x2',
- hidden=True,
- colors=["#0606FF", "#0606A0"],
- style='portfolio-style-2x2',
- label=' ',
- prim_name='t2x2',
- default=[_('Title'), 'None', 'None', 'None', 'None'],
- special_name=_('presentation 2x2'),
- string_or_number=True,
- help_string=_("presentation template: select four \
-Journal objects"))
- self.tw.lc.def_prim('t2x2', 5,
- lambda self, a, b, c, d, e:
- primitive_dictionary['t2x2'](a, b, c, d, e))
-
- palette.add_block('templatelist',
- hidden=True,
- colors=["#0606FF", "#0606A0"],
- style='bullet-style',
- label=' ',
- prim_name='bullet',
- default=[_('Title'), '∙ '],
- special_name=_('presentation bulleted list'),
- string_or_number=True,
- help_string=_('presentation template: list of \
-bullets'))
- self.tw.lc.def_prim('bullet', 1, self._prim_list, True)
-
def _myblocks_palette(self):
''' User-defined macros are saved as a json-encoded file;
these get loaded into a palette on startup '''
@@ -1244,13 +1133,13 @@ bullets'))
def _prim_readpixel(self):
""" Read r, g, b, a from the canvas and push b, g, r to the stack """
- r, g, b, a = self.tw.canvas.get_pixel()
+ r, g, b, a = self.tw.turtles.get_active_turtle().get_pixel()
self.tw.lc.heap.append(b)
self.tw.lc.heap.append(g)
self.tw.lc.heap.append(r)
def _prim_active_turtle(self):
- return(self.tw.active_turtle.name)
+ return(self.tw.turtles.get_active_turtle().get_name())
def _prim_reskin(self, media):
""" Reskin the turtle with an image from a file """
@@ -1282,13 +1171,13 @@ bullets'))
debug_output("Couldn't open skin %s" % (self.tw.lc.filepath),
self.tw.running_sugar)
if pixbuf is not None:
- self.tw.active_turtle.set_shapes([pixbuf])
- pen_state = self.tw.active_turtle.get_pen_state()
+ self.tw.turtles.get_active_turtle().set_shapes([pixbuf])
+ pen_state = self.tw.turtles.get_active_turtle().get_pen_state()
if pen_state:
- self.tw.canvas.setpen(False)
- self.tw.canvas.forward(0)
+ self.tw.turtles.get_active_turtle().set_pen_state(False)
+ self.tw.turtles.get_active_turtle().forward(0)
if pen_state:
- self.tw.canvas.setpen(True)
+ self.tw.turtles.get_active_turtle().set_pen_state(True)
if self.tw.sharing():
if self.tw.running_sugar:
@@ -1428,7 +1317,7 @@ bullets'))
def _prim_see(self):
""" Read r, g, b from the canvas and return a corresponding palette
color """
- r, g, b, a = self.tw.canvas.get_pixel()
+ r, g, b, a = self.tw.turtles.get_active_turtle().get_pixel()
color_index = self.tw.canvas.get_color_index(r, g, b)
if self.tw.lc.update_values:
self.tw.lc.update_label_value('see', color_index)
@@ -1498,7 +1387,7 @@ bullets'))
x, y = self.tw.lc.x2tx(), self.tw.lc.y2ty()
if center:
y -= self.tw.canvas.textsize
- self.tw.canvas.draw_text(string, x, y,
+ self.tw.turtles.get_active_turtle().draw_text(string, x, y,
int(self.tw.canvas.textsize *
self.tw.lc.scale / 100.),
self.tw.canvas.width - x)
@@ -1507,17 +1396,17 @@ bullets'))
x, y = self.tw.lc.x2tx(), self.tw.lc.y2ty()
if center:
y -= self.tw.canvas.textsize
- self.tw.canvas.draw_text(string, x, y,
+ self.tw.turtles.get_active_turtle().draw_text(string, x, y,
int(self.tw.canvas.textsize *
self.tw.lc.scale / 100.),
self.tw.canvas.width - x)
def _prim_showlist(self, sarray):
""" Display list of media objects """
- x = self.tw.canvas.xcor / self.tw.coord_scale
- y = self.tw.canvas.ycor / self.tw.coord_scale
+ x = self.tw.turtles.get_active_turtle.get_xy()[0] / self.tw.coord_scale
+ y = self.tw.turtles.get_active_turtle.get_xy()[1] / self.tw.coord_scale
for s in sarray:
- self.tw.canvas.setxy(x, y, pendown=False)
+ self.tw.turtles.get_active_turtle().set_xy(x, y, pendown=False)
self._prim_show(s)
y -= int(self.tw.canvas.textsize * self.tw.lead)
@@ -1576,14 +1465,14 @@ bullets'))
# Place the block at the active turtle (x, y) and move the turtle
# into position to place the next block in the stack.
# TODO: Add expandable argument
- x, y = self.tw.active_turtle.get_xy()
+ pos = self.tw.turtles.get_active_turtle().get_xy()
if isinstance(blkname, list):
name = blkname[0]
if len(blkname) > 1:
value = blkname[1:]
- dy = int(self._find_block(name, x, y, value))
+ dy = int(self._find_block(name, pos[0], pos[1], value))
else:
- dy = int(self._find_block(name, x, y))
+ dy = int(self._find_block(name, pos[0], pos[1]))
else:
name = blkname
if name == 'delete':
@@ -1593,11 +1482,12 @@ bullets'))
blk.spr.hide()
dy = 0
else:
- dy = int(self._find_block(name, x, y))
+ dy = int(self._find_block(name, pos[0], pos[1]))
# Reposition turtle to end of flow
- self.tw.canvas.ycor -= dy
- self.tw.canvas.move_turtle()
+ pos = self.tw.turtles.get_active_turtle().get_xy()
+ pos[1] -= dy
+ self.tw.turtles.get_active_turtle().move_turtle(pos)
def _make_block(self, name, x, y, defaults):
if defaults is None:
@@ -1664,170 +1554,3 @@ bullets'))
self.tw.show_toolbar_palette(palette_name_to_index(arg))
else:
raise logoerror("#syntaxerror")
-
- # Deprecated blocks
-
- def _prim_t1x1(self, title, media):
- """ title, one image, and description """
- xo = self.tw.calc_position('t1x1')[2]
- x = -(self.tw.canvas.width / 2) + xo
- y = self.tw.canvas.height / 2
- self.tw.canvas.setxy(x, y, pendown=False)
- # save the text size so we can restore it later
- save_text_size = self.tw.canvas.textsize
- # set title text
- self.tw.canvas.settextsize(self.title_height)
- self._prim_show(title)
- # calculate and set scale for media blocks
- myscale = 45 * (self.tw.canvas.height - self.title_height * 2) \
- / self.tw.canvas.height
- self._prim_setscale(myscale)
- # set body text size
- self.tw.canvas.settextsize(self.tw.lc.body_height)
- # render media object
- # leave some space below the title
- y -= int(self.title_height * 2 * self.tw.lead)
- self.tw.canvas.setxy(x, y, pendown=False)
- self._prim_show(media)
- if self.tw.running_sugar:
- x = 0
- self.tw.canvas.setxy(x, y, pendown=False)
- self._prim_show(media.replace('media_', 'descr_'))
- # restore text size
- self.tw.canvas.settextsize(save_text_size)
-
- def _prim_t2x1(self, title, media1, media2):
- """ title, two images (horizontal), two descriptions """
- xo = self.tw.calc_position('t2x1')[2]
- x = -(self.tw.canvas.width / 2) + xo
- y = self.tw.canvas.height / 2
- self.tw.canvas.setxy(x, y, pendown=False)
- # save the text size so we can restore it later
- save_text_size = self.tw.canvas.textsize
- # set title text
- self.tw.canvas.settextsize(self.title_height)
- self._prim_show(title)
- # calculate and set scale for media blocks
- myscale = 45 * (self.tw.canvas.height - self.title_height * 2) / \
- self.tw.canvas.height
- self._prim_setscale(myscale)
- # set body text size
- self.tw.canvas.settextsize(self.tw.lc.body_height)
- # render four quadrents
- # leave some space below the title
- y -= int(self.title_height * 2 * self.tw.lead)
- self.tw.canvas.setxy(x, y, pendown=False)
- self._prim_show(media1)
- x = 0
- self.tw.canvas.setxy(x, y, pendown=False)
- self._prim_show(media2)
- y = -self.title_height
- if self.tw.running_sugar:
- self.tw.canvas.setxy(x, y, pendown=False)
- self._prim_show(media2.replace('media_', 'descr_'))
- x = -(self.tw.canvas.width / 2) + xo
- self.tw.canvas.setxy(x, y, pendown=False)
- self._prim_show(media1.replace('media_', 'descr_'))
- # restore text size
- self.tw.canvas.settextsize(save_text_size)
-
- def _prim_t1x2(self, title, media1, media2):
- """ title, two images (vertical), two desciptions """
- xo = self.tw.calc_position('t1x2')[2]
- x = -(self.tw.canvas.width / 2) + xo
- y = self.tw.canvas.height / 2
- self.tw.canvas.setxy(x, y, pendown=False)
- # save the text size so we can restore it later
- save_text_size = self.tw.canvas.textsize
- # set title text
- self.tw.canvas.settextsize(self.title_height)
- self._prim_show(title)
- # calculate and set scale for media blocks
- myscale = 45 * (self.tw.canvas.height - self.title_height * 2) / \
- self.tw.canvas.height
- self._prim_setscale(myscale)
- # set body text size
- self.tw.canvas.settextsize(self.tw.lc.body_height)
- # render four quadrents
- # leave some space below the title
- y -= int(self.title_height * 2 * self.tw.lead)
- self.tw.canvas.setxy(x, y, pendown=False)
- self._prim_show(media1)
- if self.tw.running_sugar:
- x = 0
- self.tw.canvas.setxy(x, y, pendown=False)
- self._prim_show(media1.replace('media_', 'descr_'))
- y = -self.title_height
- self.tw.canvas.setxy(x, y, pendown=False)
- self._prim_show(media2.replace('media_', 'descr_'))
- x = -(self.tw.canvas.width / 2) + xo
- self.tw.canvas.setxy(x, y, pendown=False)
- self._prim_show(media2)
- # restore text size
- self.tw.canvas.settextsize(save_text_size)
-
- def _prim_t2x2(self, title, media1, media2, media3, media4):
- """ title and four images """
- xo = self.tw.calc_position('t2x2')[2]
- x = -(self.tw.canvas.width / 2) + xo
- y = self.tw.canvas.height / 2
- self.tw.canvas.setxy(x, y, pendown=False)
- # save the text size so we can restore it later
- save_text_size = self.tw.canvas.textsize
- # set title text
- self.tw.canvas.settextsize(self.title_height)
- self._prim_show(title)
- # calculate and set scale for media blocks
- myscale = 45 * (self.tw.canvas.height - self.title_height * 2) / \
- self.tw.canvas.height
- self._prim_setscale(myscale)
- # set body text size
- self.tw.canvas.settextsize(self.tw.lc.body_height)
- # render four quadrents
- # leave some space below the title
- y -= int(self.title_height * 2 * self.tw.lead)
- self.tw.canvas.setxy(x, y, pendown=False)
- self._prim_show(media1)
- x = 0
- self.tw.canvas.setxy(x, y, pendown=False)
- self._prim_show(media2)
- y = -self.title_height
- self.tw.canvas.setxy(x, y, pendown=False)
- self._prim_show(media4)
- x = -(self.tw.canvas.width / 2) + xo
- self.tw.canvas.setxy(x, y, pendown=False)
- self._prim_show(media3)
- # restore text size
- self.tw.canvas.settextsize(save_text_size)
-
- def _prim_t1x1a(self, title, media1):
- """ title, one media object """
- xo = self.tw.calc_position('t1x1a')[2]
- x = -(self.tw.canvas.width / 2) + xo
- y = self.tw.canvas.height / 2
- self.tw.canvas.setxy(x, y, pendown=False)
- # save the text size so we can restore it later
- save_text_size = self.tw.canvas.textsize
- # set title text
- self.tw.canvas.settextsize(self.title_height)
- self._prim_show(title)
- # calculate and set scale for media blocks
- myscale = 90 * (self.tw.canvas.height - self.title_height * 2) / \
- self.tw.canvas.height
- self._prim_setscale(myscale)
- # set body text size
- self.tw.canvas.settextsize(self.tw.lc.body_height)
- # render media object
- # leave some space below the title
- y -= int(self.title_height * 2 * self.tw.lead)
- self.tw.canvas.setxy(x, y, pendown=False)
- self._prim_show(media1)
- # restore text size
- self.tw.canvas.settextsize(save_text_size)
-
- def _prim_write(self, string, fsize):
- """ Write string at size """
- x = self.tw.canvas.width / 2 + int(self.tw.canvas.xcor)
- y = self.tw.canvas.height / 2 - int(self.tw.canvas.ycor)
- self.tw.canvas.draw_text(string, x, y - 15, int(fsize),
- self.tw.canvas.width)
diff --git a/pysamples/dotted_line.py b/pysamples/dotted_line.py
index febd409..c581f83 100644
--- a/pysamples/dotted_line.py
+++ b/pysamples/dotted_line.py
@@ -1,4 +1,4 @@
-#Copyright (c) 2009-11, Walter Bender
+#Copyright (c) 2009-13, Walter Bender
# This procedure is invoked when the user-definable block on the "extras"
# palette is selected. Some examples of how to use this block are included
@@ -52,43 +52,57 @@
# clearscreen(self) tw.canvas.clearscreen()
# Note: Clears the screen and resets all turtle and
# pen attributes to default values
-# setpen(self, flag) tw.canvas.setpen(True)
+#
+#
+# Class Turtles -- useful properties and methods (from within
+# tamyblock.py, tw.turtles is the class instance)
+#
+# self.set_turtle(name) tw.turltes.set_turtle(1)
+# Note: Set the current turtle to turtle '1'
+# self.get_active_turtle() tw.turltes.get_active_turtle()
+# Note: Returns active turtle (See Class Turtle below)
+#
+# Class Turtle -- useful properties and methods (from within
+# tamyblock.py, tw.turtles.get_active_turtle() is the class instance)
+#
+# set_pen_state(self, flag)
+# tw.turtles.get_active_turtle().set_pen_state(True)
# Note: True will set the pen "down", enabling drawing;
# False will set the pen "up"
-# forward(self, n) tw.canvas.forward(100)
+# forward(self, n) tw.turtles.get_active_turtle().forward(100)
# Note: Move the turtle forward 100 units
-# arc(self, a, r) tw.canvas.arc(120, 50)
+# arc(self, a, r) tw.turtles.get_active_turtle().arc(120, 50)
# Note: Move the turtle along an arc of 120 degrees
# (clockwise) and radius of 50 units
-# setheading(self, a) tw.canvas.setheading(180)
+# set_heading(self, a) tw.turtles.get_active_turtle().set_heading(180)
# Note: Set the turtle heading to 180
# (towards the bottom of the screen)
-# self.heading tw.canvas.heading
+# self.get_heading() tw.turtles.get_active_turtle().get_heading()
# Note: The current heading
-# setpensize(self, n) tw.canvas.setpensize(25)
+# set_pen_size(self, n) tw.turtles.get_active_turtle().set_pen_size(25)
# Note: Set the turtle pensize to 25 units
-# self.pensize tw.canvas.pensize
-# Note: The current pensize
-# setcolor(self, c) tw.canvas.color(70)
+# self.get_pen_size() tw.turtles.get_active_turtle().get_pen_size()
+# Note: The current pen size
+# self.set_color(self, c) tw.turtles.get_active_turtle().set_color(70)
# Note: Set the pen color to 70 (blue)
-# self.color tw.canvas.color
+# self.color() tw.turtles.get_active_turtle().get_color()
# Note: The current pen color
-# setshade(self, s) tw.canvas.shade(50)
+# self.set_shade(self, s) tw.turtles.get_active_turtle().set_shade(50)
# Note: Set the pen shade to 50
-# self.shade tw.canvas.shade
+# self.get_shade() tw.turtles.get_active_turtle().get_shade()
# Note: The current pen shade
-# fillscreen(self, c, s) tw.canvas.fillscreen(70, 90)
+# fillscreen(self, c, s)
+# tw.turtles.get_active_turtle().fillscreen(70, 90)
# Note: Fill the screen with color 70, shade 90 (light blue)
-# setxy(self, x, y) tw.canvas.setxy(100,100)
+# self.set_xy(self, (x, y))
+# tw.turtles.get_active_turtle().set_xy((100,100))
# Note: Move the turtle to position (100, 100)
-# self.xcor tw.canvas.xcor
+# self.get_xy tw.turtles.get_active_turtle().get_xy()[0]
# Note: The current x coordinate of the turtle
# (scaled to current units)
-# self.ycor tw.canvas.ycor
+# self.get_xy tw.turtles.get_active_turtle().get_xy()[1]
# Note: The current y coordinate of the turtle
# (scaled to current units)
-# self.set_turtle(name) tw.canvas.set_turtle(1)
-# Note: Set the current turtle to turtle '1'
#
#
# Other useful Python functions
@@ -117,17 +131,18 @@ def myblock(tw, line_length):
line_length = float(line_length)
except ValueError:
return
- if tw.canvas.pendown:
+ if tw.turtles.get_active_turtle().get_pen_state():
dist = 0
- while dist + tw.canvas.pensize < line_length: # repeat drawing dots
- tw.canvas.setpen(True)
- tw.canvas.forward(1)
- tw.canvas.setpen(False)
- tw.canvas.forward((tw.canvas.pensize * 2) - 1)
- dist += (tw.canvas.pensize * 2)
+ pen_size = tw.turtles.get_active_turtle().get_pen_size()
+ while dist + pen_size < line_length: # repeat drawing dots
+ tw.turtles.get_active_turtle().set_pen_state(True)
+ tw.turtles.get_active_turtle().forward(1)
+ tw.turtles.get_active_turtle().set_pen_state(False)
+ tw.turtles.get_active_turtle().forward(pen_size * 2 - 1)
+ dist += pen_size * 2
# make sure we have moved exactly line_length
- tw.canvas.forward(line_length - dist)
- tw.canvas.setpen(True)
+ tw.turtles.get_active_turtle().forward(line_length - dist)
+ tw.turtles.get_active_turtle().set_pen_state(True)
else:
- tw.canvas.forward(line_length)
+ tw.turtles.get_active_turtle().forward(line_length)
return
diff --git a/pysamples/forward_push.py b/pysamples/forward_push.py
index b98c726..ca527db 100644
--- a/pysamples/forward_push.py
+++ b/pysamples/forward_push.py
@@ -1,10 +1,13 @@
-#Copyright (c) 2012, Walter Bender
+#Copyright (c) 2012-2013, Walter Bender
# Usage: Import this code into a Python (user-definable) block; when
-# this code is run, the turtle will draw a line of the length of the
-# numeric argument block docked to the Python block. But before
-# drawing the line, it pushes the rgb values of the destination to the
-# FILO.
+# this code is run, a new block will be added to the Turtle Palette.
+# This block will be named 'name', the value of the argument block
+# docked to the Python block.
+#
+# The new block will cause the turtle to draw a line of the
+# length. But before drawing the line, it pushes the rgb values of the
+# destination to the FILO.
def myblock(tw, name):
@@ -15,16 +18,16 @@ def myblock(tw, name):
line_length = float(line_length)
except ValueError:
return
- penstatus = tw.canvas.pendown
- tw.canvas.setpen(False)
- tw.canvas.forward(line_length)
- r, g, b, a = tw.canvas.get_pixel()
+ penstatus = tw.turtles.get_active_turtle().get_pen_status()
+ tw.turtles.get_active_turtle().set_pen_state(False)
+ tw.turtles.get_active_turtle().forward(line_length)
+ r, g, b, a = tw.turtles.get_active_turtle().get_pixel()
tw.lc.heap.append(b)
tw.lc.heap.append(g)
tw.lc.heap.append(r)
- tw.canvas.forward(-line_length)
- tw.canvas.setpen(penstatus)
- tw.canvas.forward(line_length)
+ tw.turtles.get_active_turtle().forward(-line_length)
+ tw.turtles.get_active_turtle().set_pen_state(penstatus)
+ tw.turtles.get_active_turtle().forward(line_length)
return
from TurtleArt.tapalette import make_palette, palette_name_to_index
diff --git a/pysamples/load_block.py b/pysamples/load_block.py
deleted file mode 100644
index a4f680d..0000000
--- a/pysamples/load_block.py
+++ /dev/null
@@ -1,90 +0,0 @@
-#Copyright (c) 2011,2012 Walter Bender
-
-# DEPRECATED by load block on extras palette.
-
-# This procedure is invoked when the user-definable block on the
-# "extras" palette is selected.
-
-# Usage: Import this code into a Python (user-definable) block; when
-# this code is run, the turtle will create a block at the current
-# location of the turtle. The first argument to the Python block
-# should be a string containing the name of the desired
-# block. Arguments to the block can be passed by expanding the Python
-# block to include up to two additional arguments. Note that the
-# turtle is moved to the bottom of the block after it is loaded in
-# order position another block to the bottom of the stack.
-
-# The status of these blocks is set to 'load block'
-
-# For example, try the following to place forward 100, right 90 on the canvas:
-# start
-# Python(forward, 100) <-- Python load_block.py expanded to two arguments
-# Python(right, 90) <-- Python load_block.py expanded to two arguments
-
-
-def myblock(tw, blkname):
- ''' Load a block on to the canvas '''
-
- from TurtleArt.tapalette import block_names, block_primitives, \
- special_names, content_blocks
- from TurtleArt.tautils import find_group
-
- def make_block(tw, name, x, y, defaults):
- tw._new_block(name, x, y, defaults)
-
- # Find the block we just created and attach it to a stack.
- tw.drag_group = None
- spr = tw.sprite_list.find_sprite((x, y))
- if spr is not None:
- blk = tw.block_list.spr_to_block(spr)
- if blk is not None:
- tw.drag_group = find_group(blk)
- for b in tw.drag_group:
- b.status = 'load block'
- tw._snap_to_dock()
-
- # Disassociate new block from mouse.
- tw.drag_group = None
- return blk.docks[-1][3]
-
- def find_block(tw, blkname, x, y, defaults=None):
- """ Create a new block. It is a bit more work than just calling
- _new_block(). We need to:
- (1) translate the label name into the internal block name;
- (2) 'dock' the block onto a stack where appropriate; and
- (3) disassociate the new block from the mouse. """
-
- for name in block_names:
- # Translate label name into block/prim name.
- if blkname in block_names[name]:
- if name in block_primitives and \
- block_primitives[name] == name:
- return make_block(tw, name, x, y, defaults)
- elif name in content_blocks:
- return make_block(tw, name, x, y, defaults)
- for name in special_names:
- # Translate label name into block/prim name.
- if blkname in special_names[name]:
- return make_block(tw, name, x, y, defaults)
- return -1
-
- # Place the block at the active turtle (x, y) and move the turtle
- # into position to place the next block in the stack.
- x, y = tw.active_turtle.get_xy()
- if isinstance(blkname, list):
- name = blkname[0]
- value = blkname[1:]
- dy = int(find_block(tw, name, x, y, value))
- else:
- name = blkname
- if name == 'delete':
- for blk in tw.just_blocks():
- if blk.status == 'load block':
- blk.type = 'trash'
- blk.spr.hide()
- dy = 0
- else:
- dy = int(find_block(tw, name, x, y))
-
- tw.canvas.ypos -= dy
- tw.canvas.move_turtle()
diff --git a/pysamples/uturn.py b/pysamples/uturn.py
index cdeb96d..fee9a29 100644
--- a/pysamples/uturn.py
+++ b/pysamples/uturn.py
@@ -1,4 +1,4 @@
-#Copyright (c) 2011, Walter Bender
+#Copyright (c) 2011-2013, Walter Bender
# This procedure is invoked when the user-definable block on the
# "extras" palette is selected.
@@ -11,6 +11,17 @@
def myblock(tw, arg):
''' Add a uturn block to the 'turtle' palette '''
+ # def_prim takes 3 arguments: the primitive name, the number of
+ # arguments -- 0 in this case -- and the function to call -- in this
+ # case, we define the _prim_uturn function to set heading += 180.
+ def _prim_uturn(tw):
+ value = tw.turtles.get_active_turtle().get_heading() + 180
+ tw.turtles.get_active_turtle().set_heading(value)
+ # We also update the label on the heading block to indicate
+ # the current heading value
+ if tw.lc.update_values:
+ tw.lc.update_label_value('heading', value)
+
from TurtleArt.tapalette import make_palette, palette_name_to_index
from TurtleArt.talogo import primitive_dictionary
from gettext import gettext as _
@@ -26,10 +37,14 @@ def myblock(tw, arg):
help_string=_('make a uturn'))
# Add its primitive to the LogoCode dictionary.
- tw.lc.def_prim('uturn', 0,
- lambda self: primitive_dictionary['set']
- ('heading', tw.canvas.seth, tw.canvas.heading + 180))
+ tw.lc.def_prim('uturn', 0, lambda self: _prim_uturn(tw))
# Regenerate the palette, which will now include the new block.
tw.show_toolbar_palette(palette_name_to_index('turtle'),
regenerate=True)
+
+ palette.add_block('uturn', # the name of your block
+ style='basic-style', # the block style
+ label=_('u turn'), # the label for the block
+ prim_name='uturn', # code reference (see below)
+ help_string=_('turns the turtle 180 degrees'))
diff --git a/samples/game-continents.ta b/samples/game-continents.ta
index f56d5d9..fbbafc2 100644
--- a/samples/game-continents.ta
+++ b/samples/game-continents.ta
@@ -1,263 +1,263 @@
-[[0, "hat", 449, 231, [null, 136, 250]],
-[1, ["storein", 0], 467, 319, [250, 42, 54, 2]],
-[2, ["storein", 0], 467, 403, [1, 43, 55, 3]],
-[3, ["storein", 0], 467, 487, [2, 4, 51, 5]],
-[4, ["string", "size"], 535, 487, [3, null]],
-[5, ["storein", 0], 467, 571, [3, 6, 48, 7]],
-[6, ["string", "name"], 535, 571, [5, null]],
-[7, ["storein", 0], 467, 655, [5, 8, 99, 9]],
-[8, ["string", "picture"], 535, 655, [7, null]],
-[9, "stack", 467, 739, [7, 151, null]],
-[10, "hat", 112, 364, [null, 155, 239]],
-[11, ["setxy2", 0], 130, 536, [252, 12, 258, 253]],
-[12, ["number", 0], 188, 536, [11, null]],
-[13, "show", 130, 704, [259, 14, 254]],
-[14, ["plus2", 0], 188, 704, [13, 15, 16]],
-[15, ["string", "Put the turtle in "], 242, 704, [14, null]],
-[16, "box", 242, 746, [14, 17, null]],
-[17, ["string", "name"], 297, 746, [16, null]],
-[18, "hat", 560, 322, [null, 138, 248]],
-[19, ["storein", 0], 578, 410, [248, 44, 56, 20]],
-[20, ["storein", 0], 578, 494, [19, 45, 57, 21]],
-[21, ["storein", 0], 578, 578, [20, 22, 52, 23]],
-[22, ["string", "size"], 646, 578, [21, null]],
-[23, ["storein", 0], 578, 662, [21, 24, 49, 25]],
-[24, ["string", "name"], 646, 662, [23, null]],
-[25, ["storein", 0], 578, 746, [23, 26, 100, 27]],
-[26, ["string", "picture"], 646, 746, [25, null]],
-[27, "stack", 578, 830, [25, 153, null]],
-[28, "hat", 451, 422, [null, 139, 247]],
-[29, ["storein", 0], 469, 510, [247, 46, 74, 30]],
-[30, ["storein", 0], 469, 594, [29, 47, 75, 31]],
-[31, ["storein", 0], 469, 678, [30, 32, 53, 33]],
-[32, ["string", "size"], 537, 678, [31, null]],
-[33, ["storein", 0], 469, 762, [31, 34, 50, 35]],
-[34, ["string", "name"], 537, 762, [33, null]],
-[35, ["storein", 0], 469, 846, [33, 36, 101, 37]],
-[36, ["string", "picture"], 537, 846, [35, null]],
-[37, "stack", 469, 930, [35, 154, null]],
-[38, "hat", 674, 214, [null, 39, 240]],
-[39, ["string", "map"], 732, 226, [38, null]],
-[40, "setscale", 692, 302, [240, 41, 165]],
-[41, ["number", 100], 769, 302, [40, null]],
-[42, ["string", "x"], 535, 319, [1, null]],
-[43, ["string", "y"], 535, 403, [2, null]],
-[44, ["string", "x"], 646, 410, [19, null]],
-[45, ["string", "y"], 646, 494, [20, null]],
-[46, ["string", "x"], 537, 510, [29, null]],
-[47, ["string", "y"], 537, 594, [30, null]],
-[48, ["string", "Africa"], 535, 613, [5, null]],
-[49, ["string", "North America"], 646, 704, [23, null]],
-[50, ["string", "Eurasia"], 537, 804, [33, null]],
-[51, ["number", 100], 535, 529, [3, null]],
-[52, ["number", 150], 646, 620, [21, null]],
-[53, ["number", 150], 537, 720, [31, null]],
-[54, ["number", 20], 535, 361, [1, null]],
-[55, ["number", 40], 535, 445, [2, null]],
-[56, ["number", -350], 646, 452, [19, null]],
-[57, ["number", 200], 646, 536, [20, null]],
-[58, "hat", 222, 251, [null, 135, 251]],
-[59, ["storein", 0], 240, 339, [251, 60, 61, 62]],
-[60, ["string", "x"], 308, 339, [59, null]],
-[61, ["number", -300], 308, 381, [59, null]],
-[62, ["storein", 0], 240, 423, [59, 63, 64, 65]],
-[63, ["string", "y"], 308, 423, [62, null]],
-[64, ["number", -50], 308, 465, [62, null]],
-[65, ["storein", 0], 240, 507, [62, 66, 67, 68]],
-[66, ["string", "size"], 308, 507, [65, null]],
-[67, ["number", 100], 308, 549, [65, null]],
-[68, ["storein", 0], 240, 591, [65, 69, 70, 71]],
-[69, ["string", "name"], 308, 591, [68, null]],
-[70, ["string", "South America"], 308, 633, [68, null]],
-[71, ["storein", 0], 240, 675, [68, 72, 102, 73]],
-[72, ["string", "picture"], 308, 675, [71, null]],
-[73, "stack", 240, 759, [71, 150, null]],
-[74, ["number", 250], 537, 552, [29, null]],
-[75, ["number", 200], 537, 636, [30, null]],
-[76, "hat", 334, 338, [null, 137, 249]],
-[77, ["storein", 0], 352, 426, [249, 78, 79, 80]],
-[78, ["string", "x"], 420, 426, [77, null]],
-[79, ["number", 390], 420, 468, [77, null]],
-[80, ["storein", 0], 352, 510, [77, 81, 82, 83]],
-[81, ["string", "y"], 420, 510, [80, null]],
-[82, ["number", -75], 420, 552, [80, null]],
-[83, ["storein", 0], 352, 594, [80, 84, 85, 86]],
-[84, ["string", "size"], 420, 594, [83, null]],
-[85, ["number", 100], 420, 636, [83, null]],
-[86, ["storein", 0], 352, 678, [83, 87, 88, 89]],
-[87, ["string", "name"], 420, 678, [86, null]],
-[88, ["string", "Australia"], 420, 720, [86, null]],
-[89, ["storein", 0], 352, 762, [86, 90, 224, 91]],
-[90, ["string", "picture"], 420, 762, [89, null]],
-[91, "stack", 352, 846, [89, 152, null]],
-[92, ["start", 2.0], 82, 255, [null, 246]],
-[93, "stack", 100, 335, [246, 94, 95]],
-[94, ["string", "setup"], 158, 335, [93, null]],
-[95, "stack", 100, 377, [93, 96, 97]],
-[96, ["string", "map"], 158, 377, [95, null]],
-[97, "stack", 100, 419, [95, 140, 98]],
-[98, "stack", 100, 461, [97, 141, 142]],
-[99, ["journal", "./samples/images/Africa.png"], 535, 697, [7, null]],
-[100, ["journal", "./samples/images/North_America.png"], 646, 788, [25, null]],
-[101, ["journal", "./samples/images/Eurasia.png"], 537, 888, [35, null]],
-[102, ["journal", "./samples/images/South_America.png"], 308, 717, [71, null]],
-[103, "hat", 692, 391, [null, 104, 245]],
-[104, ["string", "star"], 750, 403, [103, null]],
-[105, "setpensize", 710, 479, [245, 106, 107]],
-[106, ["number", 15], 812, 479, [105, null]],
-[107, ["setxy2", 0], 710, 521, [105, 108, 109, 110]],
-[108, ["number", 0], 768, 521, [107, null]],
-[109, ["number", 0], 768, 563, [107, null]],
-[110, ["repeat", 63], 710, 605, [107, 111, 226, 225]],
-[111, ["number", 5], 769, 605, [110, null]],
-[112, "setcolor", 728, 689, [226, 113, 114]],
-[113, "heading", 805, 689, [112, null]],
-[114, "forward", 728, 731, [112, 115, 116]],
-[115, ["number", 200], 799, 731, [114, null]],
-[116, "right", 728, 773, [114, 117, null]],
-[117, ["number", 144], 786, 773, [116, null]],
-[118, ["setxy2", 20.0], 130, 788, [254, 233, 234, 255]],
-[119, ["number", -150], 242, 788, [233, null]],
-[120, ["number", 100], 242, 870, [234, null]],
-[121, ["forever", 252], 130, 954, [255, 122, null]],
-[122, "stack", 148, 988, [121, 123, 230]],
-[123, ["string", "calculate distance"], 206, 988, [122, null]],
-[124, ["if", 147], 148, 1072, [230, 125, 256, 133]],
-[125, ["less2", 0], 204, 1038, [124, 126, 128, null]],
-[126, "box", 250, 1038, [125, 127, null]],
-[127, ["string", "distance"], 305, 1038, [126, null]],
-[128, ["product2", 0], 274, 1080, [125, 129, 131]],
-[129, "box", 328, 1080, [128, 130, null]],
-[130, ["string", "size"], 383, 1080, [129, null]],
-[131, "box", 328, 1122, [128, 132, null]],
-[132, ["string", "scale"], 383, 1122, [131, null]],
-[133, "wait", 148, 1492, [124, 134, null]],
-[134, ["number", 1], 206, 1492, [133, null]],
-[135, ["string", "Q1"], 280, 263, [58, null]],
-[136, ["string", "Q2"], 507, 243, [0, null]],
-[137, ["string", "Q3"], 392, 350, [76, null]],
-[138, ["string", "Q4"], 618, 334, [18, null]],
-[139, ["string", "Q5"], 509, 434, [28, null]],
-[140, ["string", "Q1"], 158, 419, [97, null]],
-[141, ["string", "Q2"], 158, 461, [98, null]],
-[142, "stack", 100, 503, [98, 144, 143]],
-[143, "stack", 100, 545, [142, 145, 146]],
-[144, ["string", "Q3"], 158, 503, [142, null]],
-[145, ["string", "Q4"], 158, 545, [143, null]],
-[146, "stack", 100, 587, [143, 149, 147]],
-[147, "stack", 100, 629, [146, 148, null]],
-[148, ["string", "star"], 158, 629, [147, null]],
-[149, ["string", "Q5"], 158, 587, [146, null]],
-[150, ["string", "go"], 298, 759, [73, null]],
-[151, ["string", "go"], 525, 739, [9, null]],
-[152, ["string", "go"], 410, 846, [91, null]],
-[153, ["string", "go"], 636, 830, [27, null]],
-[154, ["string", "go"], 527, 930, [37, null]],
-[155, ["string", "go"], 170, 376, [10, null]],
-[156, ["setxy2", 0], 166, 1180, [256, 157, 158, 257]],
-[157, ["number", 0], 224, 1180, [156, null]],
-[158, ["number", 0], 224, 1222, [156, null]],
-[159, "setscale", 166, 1306, [257, 160, 261]],
-[160, ["number", 100], 243, 1306, [159, null]],
-[161, "show", 166, 1390, [261, 162, 164]],
-[162, "box", 224, 1390, [161, 163, null]],
-[163, ["string", "picture"], 279, 1390, [162, null]],
-[164, "stopstack", 166, 1432, [161, null]],
-[165, ["setxy2", 0], 692, 344, [40, 166, 167, 168]],
-[166, ["number", 0], 750, 344, [165, null]],
-[167, ["number", 0], 750, 386, [165, null]],
-[168, "show", 692, 428, [165, 169, null]],
-[169, ["journal", "./samples/images/World_Map.png"], 750, 428, [168, null]],
-[170, "hat", 237, 437, [null, 171, 244]],
-[171, ["string", "setup"], 295, 449, [170, null]],
-[172, ["storein", 0], 255, 525, [244, 173, 174, 229]],
-[173, ["string", "scale"], 323, 525, [172, null]],
-[174, ["division2", 0], 323, 567, [172, 175, 176]],
-[175, "height", 377, 567, [174, null]],
-[176, ["number", 900], 401, 609, [174, null]],
-[177, ["storein", 0], 255, 651, [229, 178, 179, 228]],
-[178, ["string", "box"], 323, 651, [177, null]],
-[179, ["product2", 0], 323, 693, [177, 180, 181]],
-[180, ["number", -200], 377, 693, [179, null]],
-[181, "box", 377, 735, [179, 182, null]],
-[182, ["string", "scale"], 432, 735, [181, null]],
-[183, "hideblocks", 255, 777, [228, 184]],
-[184, "clean", 255, 819, [183, 185]],
-[185, "settextsize", 255, 861, [184, 186, null]],
-[186, ["product2", 0], 359, 861, [185, 187, 188]],
-[187, ["number", 24], 413, 861, [186, null]],
-[188, "box", 413, 903, [186, 189, null]],
-[189, ["string", "scale"], 468, 903, [188, null]],
-[190, "hat", 7, 459, [null, 191, 241]],
-[191, ["string", "calculate distance"], 65, 471, [190, null]],
-[192, ["storein", 0], 25, 547, [241, 193, 194, 243]],
-[193, ["string", "dx"], 93, 547, [192, null]],
-[194, ["minus2", 20.0], 93, 589, [192, 195, 200]],
-[195, ["product2", 0], 147, 589, [194, 196, 198]],
-[196, "box", 201, 589, [195, 197, null]],
-[197, ["string", "x"], 256, 589, [196, null]],
-[198, "box", 201, 631, [195, 199, null]],
-[199, ["string", "scale"], 256, 631, [198, null]],
-[200, "xcor", 171, 671, [194, null]],
-[201, ["storein", 0], 25, 713, [243, 202, 203, 242]],
-[202, ["string", "dy"], 93, 713, [201, null]],
-[203, ["minus2", 20.0], 93, 755, [201, 204, 209]],
-[204, ["product2", 0], 147, 755, [203, 205, 207]],
-[205, "box", 201, 755, [204, 206, null]],
-[206, ["string", "y"], 256, 755, [205, null]],
-[207, "box", 201, 797, [204, 208, null]],
-[208, ["string", "scale"], 256, 797, [207, null]],
-[209, "ycor", 171, 837, [203, null]],
-[210, ["storein", 0], 25, 879, [242, 211, 212, null]],
-[211, ["string", "distance"], 93, 879, [210, null]],
-[212, "sqrt", 93, 921, [210, 213]],
-[213, ["plus2", 20.0], 147, 921, [212, 214, 219]],
-[214, ["product2", 0], 201, 921, [213, 215, 217]],
-[215, "box", 255, 921, [214, 216, null]],
-[216, ["string", "dx"], 310, 921, [215, null]],
-[217, "box", 255, 963, [214, 218, null]],
-[218, ["string", "dx"], 310, 963, [217, null]],
-[219, ["product2", 0], 201, 1003, [213, 220, 222]],
-[220, "box", 255, 1003, [219, 221, null]],
-[221, ["string", "dy"], 310, 1003, [220, null]],
-[222, "box", 255, 1045, [219, 223, null]],
-[223, ["string", "dy"], 310, 1045, [222, null]],
-[224, ["journal", "./samples/images/Australia.png"], 420, 804, [89, null]],
-[225, "showblocks", 710, 833, [110, null]],
-[226, "wait", 728, 647, [110, 227, 112]],
-[227, ["number", 1], 786, 647, [226, null]],
-[228, ["vspace", 0], 255, 735, [177, 183]],
-[229, ["vspace", 0], 255, 609, [172, 177]],
-[230, ["vspace", 0], 148, 1030, [122, 124]],
-[231, "setscale", 130, 452, [239, 232, 252]],
-[232, ["number", 50], 207, 452, [231, null]],
-[233, ["product2", 0], 188, 788, [118, 119, 237]],
-[234, ["product2", 0], 188, 870, [118, 120, 235]],
-[235, "box", 242, 912, [234, 236, null]],
-[236, ["string", "scale"], 297, 912, [235, null]],
-[237, "box", 242, 830, [233, 238, null]],
-[238, ["string", "scale"], 297, 830, [237, null]],
-[239, "sandwichclampcollapsed", 112, 418, [10, 231, null]],
-[240, "sandwichclampcollapsed", 674, 268, [38, 40, null]],
-[241, "sandwichclampcollapsed", 7, 513, [190, 192, null]],
-[242, ["vspace", 20], 25, 797, [201, 210]],
-[243, ["vspace", 20], 25, 631, [192, 201]],
-[244, "sandwichclampcollapsed", 237, 491, [170, 172, null]],
-[245, "sandwichclampcollapsed", 692, 445, [103, 105, null]],
-[246, "sandwichclampcollapsed", 82, 301, [92, 93, null]],
-[247, "sandwichclampcollapsed", 451, 476, [28, 29, null]],
-[248, "sandwichclampcollapsed", 560, 376, [18, 19, null]],
-[249, "sandwichclampcollapsed", 334, 392, [76, 77, null]],
-[250, "sandwichclampcollapsed", 449, 285, [0, 1, null]],
-[251, "sandwichclampcollapsed", 222, 305, [58, 59, null]],
-[252, "penup", 130, 494, [231, 11]],
-[253, "pendown", 130, 620, [11, 259]],
-[254, "penup", 130, 746, [13, 118]],
-[255, "pendown", 130, 912, [118, 121]],
-[256, "penup", 166, 1138, [124, 156]],
-[257, "pendown", 166, 1264, [156, 159]],
-[258, ["number", 0], 188, 578, [11, null]],
-[259, "seth", 130, 662, [253, 260, 13]],
-[260, ["number", 0], 188, 662, [259, null]],
-[261, "seth", 166, 1348, [159, 262, 161]],
-[262, ["number", 0], 224, 1348, [261, null]]]
+[[0, "hat", 380, 300, [null, 132, 238]],
+[1, ["storein", 0], 398, 388, [238, 40, 52, 2]],
+[2, ["storein", 0], 398, 472, [1, 41, 53, 3]],
+[3, ["storein", 0], 398, 556, [2, 4, 49, 5]],
+[4, ["string", "size"], 465, 556, [3, null]],
+[5, ["storein", 0], 398, 640, [3, 6, 46, 7]],
+[6, ["string", "name"], 465, 640, [5, null]],
+[7, ["storein", 0], 398, 724, [5, 8, 98, 9]],
+[8, ["string", "picture"], 465, 724, [7, null]],
+[9, "stack", 398, 808, [7, 147, null]],
+[10, "hat", 896, 177, [null, 151, 228]],
+[11, ["setxy2", 0], 914, 685, [240, 12, 246, 241]],
+[12, ["number", 0], 972, 685, [11, null]],
+[13, "show", 914, 853, [247, 14, 242]],
+[14, ["plus2", 0], 972, 853, [13, 15, 16]],
+[15, ["string", "Put the turtle in "], 1026, 853, [14, null]],
+[16, "box", 1026, 895, [14, 17, null]],
+[17, ["string", "name"], 1080, 895, [16, null]],
+[18, "hat", 380, 520, [null, 134, 236]],
+[19, ["storein", 0], 398, 608, [236, 42, 54, 20]],
+[20, ["storein", 0], 398, 692, [19, 43, 55, 21]],
+[21, ["storein", 0], 398, 776, [20, 22, 50, 23]],
+[22, ["string", "size"], 465, 776, [21, null]],
+[23, ["storein", 0], 398, 860, [21, 24, 47, 25]],
+[24, ["string", "name"], 465, 860, [23, null]],
+[25, ["storein", 0], 398, 944, [23, 26, 214, 27]],
+[26, ["string", "picture"], 465, 944, [25, null]],
+[27, "stack", 398, 1028, [25, 149, null]],
+[28, "hat", 380, 640, [null, 135, 235]],
+[29, ["storein", 0], 398, 728, [235, 44, 72, 30]],
+[30, ["storein", 0], 398, 812, [29, 45, 73, 31]],
+[31, ["storein", 0], 398, 896, [30, 32, 51, 33]],
+[32, ["string", "size"], 465, 896, [31, null]],
+[33, ["storein", 0], 398, 980, [31, 34, 48, 35]],
+[34, ["string", "name"], 465, 980, [33, null]],
+[35, ["storein", 0], 398, 1064, [33, 36, 96, 37]],
+[36, ["string", "picture"], 465, 1064, [35, null]],
+[37, "stack", 398, 1148, [35, 150, null]],
+[38, "setscale", 918, 488, [233, 39, 261]],
+[39, ["number", 100], 995, 488, [38, null]],
+[40, ["string", "x"], 465, 388, [1, null]],
+[41, ["string", "y"], 465, 472, [2, null]],
+[42, ["string", "x"], 465, 608, [19, null]],
+[43, ["string", "y"], 465, 692, [20, null]],
+[44, ["string", "x"], 465, 728, [29, null]],
+[45, ["string", "y"], 465, 812, [30, null]],
+[46, ["string", "Africa"], 465, 682, [5, null]],
+[47, ["string", "North America"], 465, 902, [23, null]],
+[48, ["string", "Eurasia"], 465, 1022, [33, null]],
+[49, ["number", 100], 465, 598, [3, null]],
+[50, ["number", 150], 465, 818, [21, null]],
+[51, ["number", 150], 465, 938, [31, null]],
+[52, ["number", 20], 465, 430, [1, null]],
+[53, ["number", 40], 465, 514, [2, null]],
+[54, ["number", -350], 465, 650, [19, null]],
+[55, ["number", 200], 465, 734, [20, null]],
+[56, "hat", 380, 180, [null, 131, 239]],
+[57, ["storein", 0], 398, 268, [239, 58, 59, 60]],
+[58, ["string", "x"], 465, 268, [57, null]],
+[59, ["number", -300], 465, 310, [57, null]],
+[60, ["storein", 0], 398, 352, [57, 61, 62, 63]],
+[61, ["string", "y"], 465, 352, [60, null]],
+[62, ["number", -50], 465, 394, [60, null]],
+[63, ["storein", 0], 398, 436, [60, 64, 65, 66]],
+[64, ["string", "size"], 465, 436, [63, null]],
+[65, ["number", 100], 465, 478, [63, null]],
+[66, ["storein", 0], 398, 520, [63, 67, 68, 69]],
+[67, ["string", "name"], 465, 520, [66, null]],
+[68, ["string", "South America"], 465, 562, [66, null]],
+[69, ["storein", 0], 398, 604, [66, 70, 165, 71]],
+[70, ["string", "picture"], 465, 604, [69, null]],
+[71, "stack", 398, 688, [69, 146, null]],
+[72, ["number", 250], 465, 770, [29, null]],
+[73, ["number", 200], 465, 854, [30, null]],
+[74, "hat", 380, 420, [null, 133, 237]],
+[75, ["storein", 0], 398, 508, [237, 76, 77, 78]],
+[76, ["string", "x"], 465, 508, [75, null]],
+[77, ["number", 390], 465, 550, [75, null]],
+[78, ["storein", 0], 398, 592, [75, 79, 80, 81]],
+[79, ["string", "y"], 465, 592, [78, null]],
+[80, ["number", -75], 465, 634, [78, null]],
+[81, ["storein", 0], 398, 676, [78, 82, 83, 84]],
+[82, ["string", "size"], 465, 676, [81, null]],
+[83, ["number", 100], 465, 718, [81, null]],
+[84, ["storein", 0], 398, 760, [81, 85, 86, 87]],
+[85, ["string", "name"], 465, 760, [84, null]],
+[86, ["string", "Australia"], 465, 802, [84, null]],
+[87, ["storein", 0], 398, 844, [84, 88, 95, 89]],
+[88, ["string", "picture"], 465, 844, [87, null]],
+[89, "stack", 398, 928, [87, 148, null]],
+[90, ["start", 2.0], 660, 180, [null, 234]],
+[91, "stack", 678, 260, [234, 92, 93]],
+[92, ["string", "setup"], 736, 260, [91, null]],
+[93, "stack", 678, 302, [91, 136, 94]],
+[94, "stack", 678, 344, [93, 137, 138]],
+[95, ["journal", "./samples/images/Africa.png"], 465, 886, [87, null]],
+[96, ["journal", "./samples/images/North_America.png"], 465, 1106, [35, null]],
+[97, ["journal", "./samples/images/Eurasia.png"], 976, 698, [164, null]],
+[98, ["journal", "./samples/images/South_America.png"], 465, 766, [7, null]],
+[99, "hat", 900, 400, [null, 100, 233]],
+[100, ["string", "game over"], 958, 412, [99, null]],
+[101, "setpensize", 918, 782, [258, 102, 103]],
+[102, ["number", 15], 1020, 782, [101, null]],
+[103, ["setxy2", 0], 918, 824, [101, 104, 105, 106]],
+[104, ["number", 0], 976, 824, [103, null]],
+[105, ["number", 0], 976, 866, [103, null]],
+[106, ["repeat", 63], 918, 908, [103, 107, 216, 215]],
+[107, ["number", 5], 976, 908, [106, null]],
+[108, "setcolor", 936, 992, [216, 109, 110]],
+[109, "heading", 1013, 992, [108, null]],
+[110, "forward", 936, 1034, [108, 111, 112]],
+[111, ["number", 200], 1006, 1034, [110, null]],
+[112, "right", 936, 1076, [110, 113, null]],
+[113, ["number", 144], 994, 1076, [112, null]],
+[114, ["setxy2", 20.0], 914, 937, [242, 222, 223, 243]],
+[115, ["number", -150], 1026, 937, [222, null]],
+[116, ["number", 100], 1026, 1019, [223, null]],
+[117, ["forever", 105], 918, 368, [260, 118, null]],
+[118, "stack", 936, 402, [117, 119, 219]],
+[119, ["string", "calculate distance"], 994, 402, [118, null]],
+[120, ["if", 0], 936, 486, [219, 121, 160, 129]],
+[121, ["less2", 0], 992, 452, [120, 122, 124, null]],
+[122, "box", 1048, 452, [121, 123, null]],
+[123, ["string", "distance"], 1102, 452, [122, null]],
+[124, ["product2", 0], 1072, 494, [121, 125, 127]],
+[125, "box", 1126, 494, [124, 126, null]],
+[126, ["string", "size"], 1180, 494, [125, null]],
+[127, "box", 1126, 536, [124, 128, null]],
+[128, ["string", "scale"], 1180, 536, [127, null]],
+[129, "wait", 936, 612, [120, 130, null]],
+[130, ["number", 1], 994, 612, [129, null]],
+[131, ["string", "Q1"], 438, 192, [56, null]],
+[132, ["string", "Q2"], 438, 312, [0, null]],
+[133, ["string", "Q3"], 438, 432, [74, null]],
+[134, ["string", "Q4"], 438, 532, [18, null]],
+[135, ["string", "Q5"], 438, 652, [28, null]],
+[136, ["string", "Q1"], 736, 302, [93, null]],
+[137, ["string", "Q2"], 736, 344, [94, null]],
+[138, "stack", 678, 386, [94, 140, 139]],
+[139, "stack", 678, 428, [138, 141, 142]],
+[140, ["string", "Q3"], 736, 386, [138, null]],
+[141, ["string", "Q4"], 736, 428, [139, null]],
+[142, "stack", 678, 470, [139, 145, 143]],
+[143, "stack", 678, 512, [142, 144, null]],
+[144, ["string", "game over"], 736, 512, [143, null]],
+[145, ["string", "Q5"], 736, 470, [142, null]],
+[146, ["string", "go"], 456, 688, [71, null]],
+[147, ["string", "go"], 456, 808, [9, null]],
+[148, ["string", "go"], 456, 928, [89, null]],
+[149, ["string", "go"], 456, 1028, [27, null]],
+[150, ["string", "go"], 456, 1148, [37, null]],
+[151, ["string", "go"], 954, 189, [10, null]],
+[152, ["setxy2", 0], 914, 307, [244, 153, 154, 245]],
+[153, ["number", 0], 972, 307, [152, null]],
+[154, ["number", 0], 972, 349, [152, null]],
+[155, "setscale", 914, 433, [245, 156, 257]],
+[156, ["number", 100], 991, 433, [155, null]],
+[157, "show", 914, 559, [249, 158, 220]],
+[158, "box", 972, 559, [157, 159, null]],
+[159, ["string", "picture"], 1026, 559, [158, null]],
+[160, "stopstack", 954, 552, [120, null]],
+[161, ["setxy2", 0], 918, 572, [261, 162, 163, 262]],
+[162, ["number", 0], 976, 572, [161, null]],
+[163, ["number", 0], 976, 614, [161, null]],
+[164, "show", 918, 698, [262, 97, 258]],
+[165, ["journal", "./samples/images/World_Map.png"], 465, 646, [69, null]],
+[166, "hat", 660, 280, [null, 167, 232]],
+[167, ["string", "setup"], 718, 292, [166, null]],
+[168, ["storein", 0], 678, 368, [232, 169, 170, 218]],
+[169, ["string", "scale"], 745, 368, [168, null]],
+[170, ["division2", 0], 745, 410, [168, 171, 172]],
+[171, "height", 815, 410, [170, null]],
+[172, ["number", 900], 839, 452, [170, null]],
+[173, ["storein", 0], 678, 494, [218, 174, 175, 179]],
+[174, ["string", "box"], 745, 494, [173, null]],
+[175, ["product2", 0], 745, 536, [173, 176, 177]],
+[176, ["number", -200], 799, 536, [175, null]],
+[177, "box", 799, 578, [175, 178, null]],
+[178, ["string", "scale"], 853, 578, [177, null]],
+[179, "clean", 678, 578, [173, 251]],
+[180, "hat", 660, 400, [null, 181, 229]],
+[181, ["string", "calculate distance"], 718, 412, [180, null]],
+[182, ["storein", 0], 678, 488, [229, 183, 184, 231]],
+[183, ["string", "dx"], 745, 488, [182, null]],
+[184, ["minus2", 20.0], 745, 530, [182, 185, 190]],
+[185, ["product2", 0], 818, 530, [184, 186, 188]],
+[186, "box", 872, 530, [185, 187, null]],
+[187, ["string", "x"], 926, 530, [186, null]],
+[188, "box", 872, 572, [185, 189, null]],
+[189, ["string", "scale"], 926, 572, [188, null]],
+[190, "xcor", 842, 612, [184, null]],
+[191, ["storein", 0], 678, 654, [231, 192, 193, 230]],
+[192, ["string", "dy"], 745, 654, [191, null]],
+[193, ["minus2", 20.0], 745, 696, [191, 194, 199]],
+[194, ["product2", 0], 818, 696, [193, 195, 197]],
+[195, "box", 872, 696, [194, 196, null]],
+[196, ["string", "y"], 926, 696, [195, null]],
+[197, "box", 872, 738, [194, 198, null]],
+[198, ["string", "scale"], 926, 738, [197, null]],
+[199, "ycor", 842, 778, [193, null]],
+[200, ["storein", 0], 678, 820, [230, 201, 202, null]],
+[201, ["string", "distance"], 745, 820, [200, null]],
+[202, "sqrt", 745, 862, [200, 203]],
+[203, ["plus2", 20.0], 799, 862, [202, 204, 209]],
+[204, ["product2", 0], 853, 862, [203, 205, 207]],
+[205, "box", 907, 862, [204, 206, null]],
+[206, ["string", "dx"], 961, 862, [205, null]],
+[207, "box", 907, 904, [204, 208, null]],
+[208, ["string", "dx"], 961, 904, [207, null]],
+[209, ["product2", 0], 853, 944, [203, 210, 212]],
+[210, "box", 907, 944, [209, 211, null]],
+[211, ["string", "dy"], 961, 944, [210, null]],
+[212, "box", 907, 986, [209, 213, null]],
+[213, ["string", "dy"], 961, 986, [212, null]],
+[214, ["journal", "./samples/images/Australia.png"], 465, 986, [25, null]],
+[215, "showblocks", 918, 1136, [106, null]],
+[216, "wait", 936, 950, [106, 217, 108]],
+[217, ["number", 1], 994, 950, [216, null]],
+[218, ["vspace", 0], 678, 452, [168, 173]],
+[219, ["vspace", 0], 936, 444, [118, 120]],
+[220, "setscale", 914, 601, [157, 221, 240]],
+[221, ["number", 50], 991, 601, [220, null]],
+[222, ["product2", 0], 972, 937, [114, 115, 226]],
+[223, ["product2", 0], 972, 1019, [114, 116, 224]],
+[224, "box", 1026, 1061, [223, 225, null]],
+[225, ["string", "scale"], 1080, 1061, [224, null]],
+[226, "box", 1026, 979, [222, 227, null]],
+[227, ["string", "scale"], 1080, 979, [226, null]],
+[228, "sandwichclampcollapsed", 896, 231, [10, 244, null]],
+[229, "sandwichclampcollapsed", 660, 454, [180, 182, null]],
+[230, ["vspace", 20], 678, 738, [191, 200]],
+[231, ["vspace", 20], 678, 572, [182, 191]],
+[232, "sandwichclampcollapsed", 660, 334, [166, 168, null]],
+[233, "sandwichclampcollapsed", 900, 454, [99, 38, null]],
+[234, "sandwichclampcollapsed", 660, 226, [90, 91, null]],
+[235, "sandwichclampcollapsed", 380, 694, [28, 29, null]],
+[236, "sandwichclampcollapsed", 380, 574, [18, 19, null]],
+[237, "sandwichclampcollapsed", 380, 474, [74, 75, null]],
+[238, "sandwichclampcollapsed", 380, 354, [0, 1, null]],
+[239, "sandwichclampcollapsed", 380, 234, [56, 57, null]],
+[240, "penup", 914, 643, [220, 11]],
+[241, "pendown", 914, 769, [11, 247]],
+[242, "penup", 914, 895, [13, 114]],
+[243, "pendown", 914, 1061, [114, 255]],
+[244, "penup", 914, 265, [228, 152]],
+[245, "pendown", 914, 391, [152, 155]],
+[246, ["number", 0], 972, 727, [11, null]],
+[247, "seth", 914, 811, [241, 248, 13]],
+[248, ["number", 0], 972, 811, [247, null]],
+[249, "seth", 914, 517, [257, 250, 157]],
+[250, ["number", 0], 972, 517, [249, null]],
+[251, "setscale", 678, 620, [179, 252, null]],
+[252, ["number", 33], 755, 620, [251, null]],
+[253, "hat", 900, 280, [null, 254, 260]],
+[254, ["string", "loop"], 958, 292, [253, null]],
+[255, "stack", 914, 1103, [243, 256, null]],
+[256, ["string", "loop"], 972, 1103, [255, null]],
+[257, ["vspace", 0], 914, 475, [155, 249]],
+[258, "wait", 918, 740, [164, 259, 101]],
+[259, ["number", 1.0], 976, 740, [258, null]],
+[260, "sandwichclampcollapsed", 900, 334, [253, 117, null]],
+[261, "penup", 918, 530, [38, 161]],
+[262, "pendown", 918, 656, [161, 164]]]
diff --git a/samples/game-find-boston.ta b/samples/game-find-boston.ta
index a585024..09860d6 100644
--- a/samples/game-find-boston.ta
+++ b/samples/game-find-boston.ta
@@ -1,379 +1,371 @@
-[[0, ["string", "North America"], 178, 536, [241, null]],
-[1, ["journal", "./samples/images/North_America.jpg"], 178, 620, [243, null]],
-[2, ["number", 150], 366, 452, [238, null]],
-[3, ["number", 114], 366, 368, [232, null]],
-[4, "hat", 123, 467, [null, 5, 358]],
-[5, ["string", "Q3"], 181, 479, [4, null]],
-[6, ["storein", 0], 106, 531, [358, 7, 211, 9]],
-[7, ["string", "x"], 174, 531, [6, null]],
-[8, ["number", 500], 362, 573, [208, null]],
-[9, ["storein", 0], 106, 615, [6, 10, 213, 12]],
-[10, ["string", "y"], 174, 615, [9, null]],
-[11, ["number", 300], 362, 657, [212, null]],
-[12, ["storein", 0], 106, 699, [9, 13, 219, 15]],
-[13, ["string", "size"], 174, 699, [12, null]],
-[14, ["number", 100], 362, 741, [218, null]],
-[15, ["storein", 0], 106, 783, [12, 16, 17, 18]],
-[16, ["string", "name"], 174, 783, [15, null]],
-[17, ["string", "New England"], 174, 825, [15, null]],
-[18, ["storein", 0], 106, 867, [15, 19, 20, 21]],
-[19, ["string", "picture"], 174, 867, [18, null]],
-[20, ["journal", "./samples/images/New-England.png"], 174, 909, [18, null]],
-[21, "stack", 106, 951, [18, 22, 220]],
-[22, ["string", "go"], 164, 951, [21, null]],
-[23, ["number", -330], 361, 729, [307, null]],
-[24, ["number", -240], 361, 813, [313, null]],
-[25, ["number", 100], 361, 897, [319, null]],
-[26, ["string", "Massachusetts"], 173, 981, [322, null]],
-[27, ["journal", "./samples/images/Massachuetts.png"], 173, 1065, [324, null]],
-[28, "hat", 654, 621, [null, 29, 353]],
-[29, ["string", "calculate distance"], 712, 633, [28, null]],
-[30, ["storein", 0], 637, 685, [353, 31, 32, 352]],
-[31, ["string", "dx"], 705, 685, [30, null]],
-[32, ["minus2", 20.0], 705, 727, [30, 33, 38]],
-[33, ["product2", 0], 759, 727, [32, 34, 36]],
-[34, "box", 813, 727, [33, 35, null]],
-[35, ["string", "x"], 868, 727, [34, null]],
-[36, "box", 813, 769, [33, 37, null]],
-[37, ["string", "scalex"], 868, 769, [36, null]],
-[38, "xcor", 783, 809, [32, null]],
-[39, ["storein", 0], 637, 851, [352, 40, 41, 351]],
-[40, ["string", "dy"], 705, 851, [39, null]],
-[41, ["minus2", 20.0], 705, 893, [39, 42, 47]],
-[42, ["product2", 0], 759, 893, [41, 43, 45]],
-[43, "box", 813, 893, [42, 44, null]],
-[44, ["string", "y"], 868, 893, [43, null]],
-[45, "box", 813, 935, [42, 46, null]],
-[46, ["string", "scaley"], 868, 935, [45, null]],
-[47, "ycor", 783, 975, [41, null]],
-[48, ["storein", 0], 637, 1017, [351, 49, 50, 202]],
-[49, ["string", "distance"], 705, 1017, [48, null]],
-[50, "sqrt", 705, 1059, [48, 51]],
-[51, ["plus2", 20.0], 759, 1059, [50, 52, 57]],
-[52, ["product2", 0], 813, 1059, [51, 53, 55]],
-[53, "box", 867, 1059, [52, 54, null]],
-[54, ["string", "dx"], 922, 1059, [53, null]],
-[55, "box", 867, 1101, [52, 56, null]],
-[56, ["string", "dx"], 922, 1101, [55, null]],
-[57, ["product2", 0], 813, 1141, [51, 58, 60]],
-[58, "box", 867, 1141, [57, 59, null]],
-[59, ["string", "dy"], 922, 1141, [58, null]],
-[60, "box", 867, 1183, [57, 61, null]],
-[61, ["string", "dy"], 922, 1183, [60, null]],
-[62, ["number", 120], 363, 868, [280, null]],
-[63, ["number", 190], 363, 952, [286, null]],
-[64, ["number", 50], 363, 1036, [292, null]],
-[65, ["string", "Boston"], 175, 1120, [295, null]],
-[66, ["journal", "./samples/images/Boston.png"], 175, 1204, [297, null]],
-[67, "hat", 1042, 788, [null, 68, 350]],
-[68, ["string", "map"], 1100, 800, [67, null]],
-[69, "setscale", 1025, 852, [350, 70, 71]],
-[70, ["number", 100], 1102, 852, [69, null]],
-[71, ["setxy2", 0], 1025, 894, [69, 72, 73, 74]],
-[72, ["number", 0], 1083, 894, [71, null]],
-[73, ["number", 0], 1083, 936, [71, null]],
-[74, "show", 1025, 978, [71, 75, 203]],
-[75, ["journal", "./samples/images/earth.png"], 1083, 978, [74, null]],
-[76, "hat", 663, 415, [null, 77, 354]],
-[77, ["string", "setup"], 721, 427, [76, null]],
-[78, ["storein", 0], 646, 479, [354, 79, 80, 339]],
-[79, ["string", "scalex"], 714, 479, [78, null]],
-[80, ["division2", 0], 714, 521, [78, 338, 81]],
-[81, ["number", 1200], 792, 563, [80, null]],
-[82, ["storein", 0], 646, 731, [344, 83, 84, 337]],
-[83, ["string", "box"], 714, 731, [82, null]],
-[84, ["product2", 0], 714, 773, [82, 85, 86]],
-[85, ["number", -260], 768, 773, [84, null]],
-[86, "box", 768, 815, [84, 87, null]],
-[87, ["string", "scalex"], 823, 815, [86, null]],
-[88, "hat", 717, 523, [null, 89, 348]],
-[89, ["string", "star"], 775, 535, [88, null]],
-[90, "setpensize", 700, 587, [348, 91, 373]],
-[91, ["number", 15], 802, 587, [90, null]],
-[92, ["setxy2", 0], 700, 671, [373, 93, 94, 374]],
-[93, ["number", 0], 758, 671, [92, null]],
-[94, ["number", 0], 758, 713, [92, null]],
-[95, ["repeat", 42], 700, 797, [374, 96, 97, 204]],
-[96, ["number", 5], 759, 797, [95, null]],
-[97, "setcolor", 718, 839, [95, 98, 99]],
-[98, "heading", 795, 839, [97, null]],
-[99, "forward", 718, 881, [97, 100, 101]],
-[100, ["number", 200], 789, 881, [99, null]],
-[101, "right", 718, 923, [99, 102, null]],
-[102, ["number", 144], 776, 923, [101, null]],
-[103, "clean", 646, 857, [337, 331]],
-[104, ["number", -200], 362, 422, [253, null]],
-[105, ["number", -220], 362, 506, [259, null]],
-[106, ["number", 200], 362, 590, [265, null]],
-[107, ["string", "USA"], 174, 674, [268, null]],
-[108, ["journal", "./samples/images/United_States.png"], 174, 758, [270, null]],
-[109, "hat", 671, 205, [null, 110, 355]],
-[110, ["string", "go"], 729, 217, [109, null]],
-[111, ["if", 0], 689, 293, [355, 112, 365, 369]],
-[112, ["greater2", 0], 745, 259, [111, 113, 115, null]],
-[113, "box", 791, 259, [112, 114, null]],
-[114, ["string", "test"], 846, 259, [113, null]],
-[115, ["number", 0], 815, 301, [112, null]],
-[116, ["setxy2", 20.0], 701, 419, [361, 117, 120, 362]],
-[117, ["minus2", 0], 759, 419, [116, 118, 186]],
-[118, "box", 813, 419, [117, 119, null]],
-[119, ["string", "x"], 868, 419, [118, null]],
-[120, "box", 759, 501, [116, 121, null]],
-[121, ["string", "y"], 814, 501, [120, null]],
-[122, ["arc", 0], 701, 585, [362, 123, 124, 188]],
-[123, ["number", 360], 759, 585, [122, null]],
-[124, "box", 759, 627, [122, 330, null]],
-[125, ["setxy2", 0], 689, 461, [369, 126, 127, 370]],
-[126, ["number", 0], 747, 461, [125, null]],
-[127, "box", 747, 503, [125, 128, null]],
-[128, ["string", "box"], 802, 503, [127, null]],
-[129, "settextsize", 689, 587, [370, 130, 349]],
-[130, ["product2", 0], 793, 587, [129, 131, 132]],
-[131, ["number", 24], 847, 587, [130, null]],
-[132, "box", 847, 629, [130, 133, null]],
-[133, ["string", "scalex"], 902, 629, [132, null]],
-[134, "show", 689, 755, [375, 135, 206]],
-[135, ["plus2", 0], 747, 755, [134, 136, 137]],
-[136, ["string", "Put the turtle in "], 801, 755, [135, null]],
-[137, "box", 801, 797, [135, 138, null]],
-[138, ["string", "name"], 856, 797, [137, null]],
-[139, ["setxy2", 0], 689, 881, [371, 140, 141, 372]],
-[140, ["number", 0], 747, 881, [139, null]],
-[141, ["number", 0], 747, 923, [139, null]],
-[142, ["forever", 231], 689, 1007, [372, 143, null]],
-[143, "stack", 707, 1041, [142, 144, 205]],
-[144, ["string", "calculate distance"], 765, 1041, [143, null]],
-[145, ["if", 126], 707, 1125, [205, 146, 154, 164]],
-[146, ["less2", 0], 763, 1091, [145, 147, 149, null]],
-[147, "box", 809, 1091, [146, 148, null]],
-[148, ["string", "distance"], 864, 1091, [147, null]],
-[149, ["product2", 0], 833, 1133, [146, 150, 152]],
-[150, "box", 887, 1133, [149, 151, null]],
-[151, ["string", "size"], 942, 1133, [150, null]],
-[152, "box", 887, 1175, [149, 153, null]],
-[153, ["string", "scalex"], 942, 1175, [152, null]],
-[154, "clean", 725, 1191, [145, 155]],
-[155, ["setxy2", 0], 725, 1233, [154, 156, 157, 158]],
-[156, ["number", 0], 783, 1233, [155, null]],
-[157, ["number", 0], 783, 1275, [155, null]],
-[158, "setscale", 725, 1317, [155, 159, 377]],
-[159, ["number", 100], 802, 1317, [158, null]],
-[160, "show", 725, 1401, [377, 161, 163]],
-[161, "box", 783, 1401, [160, 162, null]],
-[162, ["string", "picture"], 838, 1401, [161, null]],
-[163, "stopstack", 725, 1443, [160, null]],
-[164, "wait", 707, 1503, [145, 165, null]],
-[165, ["number", 1], 765, 1503, [164, null]],
-[166, ["start", 2.0], 407, 211, [null, 167]],
-[167, ["storein", 0], 407, 257, [166, 168, 169, 170]],
-[168, ["string", "test"], 475, 257, [167, null]],
-[169, ["number", 0], 475, 299, [167, null]],
-[170, "stack", 407, 341, [167, 171, 172]],
-[171, ["string", "setup"], 465, 341, [170, null]],
-[172, "stack", 407, 383, [170, 173, 174]],
-[173, ["string", "map"], 465, 383, [172, null]],
-[174, "stack", 407, 425, [172, 175, 176]],
-[175, ["string", "Q1"], 465, 425, [174, null]],
-[176, "stack", 407, 467, [174, 177, 178]],
-[177, ["string", "Q2"], 465, 467, [176, null]],
-[178, "stack", 407, 509, [176, 179, 180]],
-[179, ["string", "Q3"], 465, 509, [178, null]],
-[180, "stack", 407, 551, [178, 181, 182]],
-[181, ["string", "Q4"], 465, 551, [180, null]],
-[182, "stack", 407, 593, [180, 183, 184]],
-[183, ["string", "Q5"], 465, 593, [182, null]],
-[184, "stack", 407, 635, [182, 185, null]],
-[185, ["string", "star"], 465, 635, [184, null]],
-[186, "box", 837, 461, [117, 187, null]],
-[187, ["string", "size"], 892, 461, [186, null]],
-[188, ["storein", 0], 701, 669, [122, 189, 190, 207]],
-[189, ["string", "box"], 769, 669, [188, null]],
-[190, ["plus2", 0], 769, 711, [188, 191, 192]],
-[191, "xcor", 823, 711, [190, null]],
-[192, "box", 823, 753, [190, 193, null]],
-[193, ["string", "size"], 878, 753, [192, null]],
-[194, "print", 701, 795, [207, 195, 368]],
-[195, ["plus2", 0], 759, 795, [194, 196, 198]],
-[196, "box", 813, 795, [195, 197, null]],
-[197, ["string", "box"], 868, 795, [196, null]],
-[198, ["plus2", 0], 813, 837, [195, 199, 200]],
-[199, ["string", ", "], 867, 837, [198, null]],
-[200, "ycor", 867, 879, [198, null]],
-[201, ["number", -170], 366, 284, [226, null]],
-[202, ["vspace", 1], 637, 1101, [48, null]],
-[203, ["vspace", 1], 1025, 1020, [74, null]],
-[204, ["vspace", 1], 700, 983, [95, null]],
-[205, ["vspace", 0], 707, 1083, [143, 145]],
-[206, ["vspace", 0], 689, 797, [134, 371]],
-[207, ["vspace", 0], 701, 753, [188, 194]],
-[208, ["product2", 0], 308, 573, [211, 8, 209]],
-[209, "box", 362, 615, [208, 210, null]],
-[210, ["string", "scalex"], 417, 615, [209, null]],
-[211, ["identity2", 40.0], 174, 573, [6, 208]],
-[212, ["product2", 0], 308, 657, [213, 11, 214]],
-[213, ["identity2", 40.0], 174, 657, [9, 212]],
-[214, "box", 362, 699, [212, 215, null]],
-[215, ["string", "scaley"], 417, 699, [214, null]],
-[216, "box", 362, 783, [218, 217, null]],
-[217, ["string", "scalex"], 417, 783, [216, null]],
-[218, ["product2", 0], 308, 741, [219, 14, 216]],
-[219, ["identity2", 40.0], 174, 741, [12, 218]],
-[220, ["vspace", 1], 106, 993, [21, null]],
-[221, "hat", 127, 178, [null, 222, 360]],
-[222, ["string", "Q1"], 185, 190, [221, null]],
-[223, ["storein", 0], 110, 242, [360, 224, 225, 229]],
-[224, ["string", "x"], 178, 242, [223, null]],
-[225, ["identity2", 40.0], 178, 284, [223, 226]],
-[226, ["product2", 0], 312, 284, [225, 201, 227]],
-[227, "box", 366, 326, [226, 228, null]],
-[228, ["string", "scalex"], 421, 326, [227, null]],
-[229, ["storein", 0], 110, 326, [223, 230, 231, 235]],
-[230, ["string", "y"], 178, 326, [229, null]],
-[231, ["identity2", 40.0], 178, 368, [229, 232]],
-[232, ["product2", 0], 312, 368, [231, 3, 233]],
-[233, "box", 366, 410, [232, 234, null]],
-[234, ["string", "scaley"], 421, 410, [233, null]],
-[235, ["storein", 0], 110, 410, [229, 236, 237, 241]],
-[236, ["string", "size"], 178, 410, [235, null]],
-[237, ["identity2", 40.0], 178, 452, [235, 238]],
-[238, ["product2", 0], 312, 452, [237, 2, 239]],
-[239, "box", 366, 494, [238, 240, null]],
-[240, ["string", "scalex"], 421, 494, [239, null]],
-[241, ["storein", 0], 110, 494, [235, 242, 0, 243]],
-[242, ["string", "name"], 178, 494, [241, null]],
-[243, ["storein", 0], 110, 578, [241, 244, 1, 245]],
-[244, ["string", "picture"], 178, 578, [243, null]],
-[245, "stack", 110, 662, [243, 246, 247]],
-[246, ["string", "go"], 168, 662, [245, null]],
-[247, ["vspace", 1], 110, 704, [245, null]],
-[248, "hat", 123, 316, [null, 249, 359]],
-[249, ["string", "Q2"], 181, 328, [248, null]],
-[250, ["storein", 0], 106, 380, [359, 251, 252, 256]],
-[251, ["string", "x"], 174, 380, [250, null]],
-[252, ["identity2", 40.0], 174, 422, [250, 253]],
-[253, ["product2", 0], 308, 422, [252, 104, 254]],
-[254, "box", 362, 464, [253, 255, null]],
-[255, ["string", "scalex"], 417, 464, [254, null]],
-[256, ["storein", 0], 106, 464, [250, 257, 258, 262]],
-[257, ["string", "y"], 174, 464, [256, null]],
-[258, ["identity2", 40.0], 174, 506, [256, 259]],
-[259, ["product2", 0], 308, 506, [258, 105, 260]],
-[260, "box", 362, 548, [259, 261, null]],
-[261, ["string", "scaley"], 417, 548, [260, null]],
-[262, ["storein", 0], 106, 548, [256, 263, 264, 268]],
-[263, ["string", "size"], 174, 548, [262, null]],
-[264, ["identity2", 40.0], 174, 590, [262, 265]],
-[265, ["product2", 0], 308, 590, [264, 106, 266]],
-[266, "box", 362, 632, [265, 267, null]],
-[267, ["string", "scalex"], 417, 632, [266, null]],
-[268, ["storein", 0], 106, 632, [262, 269, 107, 270]],
-[269, ["string", "name"], 174, 632, [268, null]],
-[270, ["storein", 0], 106, 716, [268, 271, 108, 272]],
-[271, ["string", "picture"], 174, 716, [270, null]],
-[272, "stack", 106, 800, [270, 273, 274]],
-[273, ["string", "go"], 164, 800, [272, null]],
-[274, ["vspace", 1], 106, 842, [272, null]],
-[275, "hat", 124, 762, [null, 276, 356]],
-[276, ["string", "Q5"], 182, 774, [275, null]],
-[277, ["storein", 0], 107, 826, [356, 278, 279, 283]],
-[278, ["string", "x"], 175, 826, [277, null]],
-[279, ["identity2", 40.0], 175, 868, [277, 280]],
-[280, ["product2", 0], 309, 868, [279, 62, 281]],
-[281, "box", 363, 910, [280, 282, null]],
-[282, ["string", "scalex"], 418, 910, [281, null]],
-[283, ["storein", 0], 107, 910, [277, 284, 285, 289]],
-[284, ["string", "y"], 175, 910, [283, null]],
-[285, ["identity2", 40.0], 175, 952, [283, 286]],
-[286, ["product2", 0], 309, 952, [285, 63, 287]],
-[287, "box", 363, 994, [286, 288, null]],
-[288, ["string", "scaley"], 418, 994, [287, null]],
-[289, ["storein", 0], 107, 994, [283, 290, 291, 295]],
-[290, ["string", "size"], 175, 994, [289, null]],
-[291, ["identity2", 40.0], 175, 1036, [289, 292]],
-[292, ["product2", 0], 309, 1036, [291, 64, 293]],
-[293, "box", 363, 1078, [292, 294, null]],
-[294, ["string", "scalex"], 418, 1078, [293, null]],
-[295, ["storein", 0], 107, 1078, [289, 296, 65, 297]],
-[296, ["string", "name"], 175, 1078, [295, null]],
-[297, ["storein", 0], 107, 1162, [295, 298, 66, 299]],
-[298, ["string", "picture"], 175, 1162, [297, null]],
-[299, "stack", 107, 1246, [297, 300, 301]],
-[300, ["string", "go"], 165, 1246, [299, null]],
-[301, ["vspace", 1], 107, 1288, [299, null]],
-[302, "hat", 122, 623, [null, 303, 357]],
-[303, ["string", "Q4"], 180, 635, [302, null]],
-[304, ["storein", 0], 105, 687, [357, 305, 306, 310]],
-[305, ["string", "x"], 173, 687, [304, null]],
-[306, ["identity2", 40.0], 173, 729, [304, 307]],
-[307, ["product2", 0], 307, 729, [306, 23, 308]],
-[308, "box", 361, 771, [307, 309, null]],
-[309, ["string", "scalex"], 416, 771, [308, null]],
-[310, ["storein", 0], 105, 771, [304, 311, 312, 316]],
-[311, ["string", "y"], 173, 771, [310, null]],
-[312, ["identity2", 40.0], 173, 813, [310, 313]],
-[313, ["product2", 0], 307, 813, [312, 24, 314]],
-[314, "box", 361, 855, [313, 315, null]],
-[315, ["string", "scaley"], 416, 855, [314, null]],
-[316, ["storein", 0], 105, 855, [310, 317, 318, 322]],
-[317, ["string", "size"], 173, 855, [316, null]],
-[318, ["identity2", 40.0], 173, 897, [316, 319]],
-[319, ["product2", 0], 307, 897, [318, 25, 320]],
-[320, "box", 361, 939, [319, 321, null]],
-[321, ["string", "scalex"], 416, 939, [320, null]],
-[322, ["storein", 0], 105, 939, [316, 323, 26, 324]],
-[323, ["string", "name"], 173, 939, [322, null]],
-[324, ["storein", 0], 105, 1023, [322, 325, 27, 326]],
-[325, ["string", "picture"], 173, 1023, [324, null]],
-[326, "stack", 105, 1107, [324, 327, 328]],
-[327, ["string", "go"], 163, 1107, [326, null]],
-[328, ["vspace", 1], 105, 1149, [326, null]],
-[329, ["vspace", 1], 646, 1025, [331, null]],
-[330, ["string", "size"], 814, 627, [124, null]],
-[331, ["if", 0], 646, 899, [103, 332, 334, 329]],
-[332, ["equal2", 0], 702, 865, [331, 335, 333, null]],
-[333, ["number", 0], 748, 907, [332, null]],
-[334, "hideblocks", 664, 965, [331, null]],
-[335, "box", 748, 865, [332, 336, null]],
-[336, ["string", "test"], 803, 865, [335, null]],
-[337, ["vspace", 0], 646, 815, [82, 103]],
-[338, "width", 768, 521, [80, null]],
-[339, ["vspace", 0], 646, 563, [78, 340]],
-[340, ["storein", 0], 646, 605, [339, 341, 342, 344]],
-[341, ["string", "scaley"], 714, 605, [340, null]],
-[342, ["division2", 0], 714, 647, [340, 345, 343]],
-[343, ["number", 900], 792, 689, [342, null]],
-[344, ["vspace", 0], 646, 689, [340, 82]],
-[345, "height", 768, 647, [342, null]],
-[346, "setscale", 689, 671, [349, 347, 375]],
-[347, ["number", 33], 766, 671, [346, null]],
-[348, "sandwichclampcollapsed", 717, 577, [88, 90, null]],
-[349, ["vspace", 0], 689, 629, [129, 346]],
-[350, "sandwichclampcollapsed", 1042, 842, [67, 69, null]],
-[351, ["vspace", 20], 637, 935, [39, 48]],
-[352, ["vspace", 20], 637, 769, [30, 39]],
-[353, "sandwichclampcollapsed", 654, 675, [28, 30, null]],
-[354, "sandwichclampcollapsed", 663, 469, [76, 78, null]],
-[355, "sandwichclampcollapsed", 671, 259, [109, 111, null]],
-[356, "sandwichclampcollapsed", 124, 816, [275, 277, null]],
-[357, "sandwichclampcollapsed", 122, 677, [302, 304, null]],
-[358, "sandwichclampcollapsed", 123, 521, [4, 6, null]],
-[359, "sandwichclampcollapsed", 123, 370, [248, 250, null]],
-[360, "sandwichclampcollapsed", 127, 232, [221, 223, null]],
-[361, "penup", 701, 377, [367, 116]],
-[362, "pendown", 701, 543, [116, 122]],
-[363, "hat", 718, 313, [null, 364, 367]],
-[364, ["string", "test"], 776, 325, [363, null]],
-[365, "stack", 707, 359, [111, 366, null]],
-[366, ["string", "test"], 765, 359, [365, null]],
-[367, "sandwichclampcollapsed", 718, 367, [363, 361, null]],
-[368, ["vspace", 1], 701, 837, [194, null]],
-[369, "penup", 689, 419, [111, 125]],
-[370, "pendown", 689, 545, [125, 129]],
-[371, "penup", 689, 839, [206, 139]],
-[372, "pendown", 689, 965, [139, 142]],
-[373, "penup", 700, 629, [90, 92]],
-[374, "pendown", 700, 755, [92, 95]],
-[375, "seth", 689, 713, [346, 376, 134]],
-[376, ["number", 0], 747, 713, [375, null]],
-[377, "seth", 725, 1359, [158, 378, 160]],
-[378, ["number", 0], 783, 1359, [377, null]]]
+[[0, ["string", "North America"], 285, 582, [229, null]],
+[1, ["journal", "./samples/images/North_America.jpg"], 285, 766, [257, null]],
+[2, ["number", 150], 473, 498, [226, null]],
+[3, ["number", 114], 473, 414, [220, null]],
+[4, "hat", 200, 400, [null, 5, 340]],
+[5, ["string", "Q3"], 258, 412, [4, null]],
+[6, ["storein", 0], 218, 488, [340, 7, 200, 9]],
+[7, ["string", "x"], 285, 488, [6, null]],
+[8, ["number", 500], 473, 530, [197, null]],
+[9, ["storein", 0], 218, 572, [6, 10, 202, 12]],
+[10, ["string", "y"], 285, 572, [9, null]],
+[11, ["number", 300], 473, 614, [201, null]],
+[12, ["storein", 0], 218, 656, [9, 13, 208, 15]],
+[13, ["string", "size"], 285, 656, [12, null]],
+[14, ["number", 100], 473, 698, [207, null]],
+[15, ["storein", 0], 218, 740, [12, 16, 17, 18]],
+[16, ["string", "name"], 285, 740, [15, null]],
+[17, ["string", "New England"], 285, 782, [15, null]],
+[18, ["storein", 0], 218, 824, [15, 19, 106, 21]],
+[19, ["string", "picture"], 285, 824, [18, null]],
+[20, ["journal", "./samples/images/New-England.png"], 285, 966, [310, null]],
+[21, "stack", 218, 908, [18, 22, null]],
+[22, ["string", "go"], 276, 908, [21, null]],
+[23, ["number", -330], 473, 630, [293, null]],
+[24, ["number", -240], 473, 714, [299, null]],
+[25, ["number", 100], 473, 798, [305, null]],
+[26, ["string", "Massachusetts"], 285, 882, [308, null]],
+[27, ["journal", "./samples/images/Massachuetts.png"], 285, 1066, [283, null]],
+[28, "hat", 860, 300, [null, 29, 335]],
+[29, ["string", "calculate distance"], 918, 312, [28, null]],
+[30, ["storein", 0], 843, 364, [335, 31, 32, 334]],
+[31, ["string", "dx"], 910, 364, [30, null]],
+[32, ["minus2", 20.0], 910, 406, [30, 33, 38]],
+[33, ["product2", 0], 983, 406, [32, 34, 36]],
+[34, "box", 1037, 406, [33, 35, null]],
+[35, ["string", "x"], 1091, 406, [34, null]],
+[36, "box", 1037, 448, [33, 37, null]],
+[37, ["string", "scalex"], 1091, 448, [36, null]],
+[38, "xcor", 1007, 488, [32, null]],
+[39, ["storein", 0], 843, 530, [334, 40, 41, 333]],
+[40, ["string", "dy"], 910, 530, [39, null]],
+[41, ["minus2", 20.0], 910, 572, [39, 42, 47]],
+[42, ["product2", 0], 983, 572, [41, 43, 45]],
+[43, "box", 1037, 572, [42, 44, null]],
+[44, ["string", "y"], 1091, 572, [43, null]],
+[45, "box", 1037, 614, [42, 46, null]],
+[46, ["string", "scaley"], 1091, 614, [45, null]],
+[47, "ycor", 1007, 654, [41, null]],
+[48, ["storein", 0], 843, 696, [333, 49, 50, 193]],
+[49, ["string", "distance"], 910, 696, [48, null]],
+[50, "sqrt", 910, 738, [48, 51]],
+[51, ["plus2", 20.0], 964, 738, [50, 52, 57]],
+[52, ["product2", 0], 1018, 738, [51, 53, 55]],
+[53, "box", 1072, 738, [52, 54, null]],
+[54, ["string", "dx"], 1126, 738, [53, null]],
+[55, "box", 1072, 780, [52, 56, null]],
+[56, ["string", "dx"], 1126, 780, [55, null]],
+[57, ["product2", 0], 1018, 820, [51, 58, 60]],
+[58, "box", 1072, 820, [57, 59, null]],
+[59, ["string", "dy"], 1126, 820, [58, null]],
+[60, "box", 1072, 862, [57, 61, null]],
+[61, ["string", "dy"], 1126, 862, [60, null]],
+[62, ["number", 120], 473, 730, [266, null]],
+[63, ["number", 190], 473, 814, [272, null]],
+[64, ["number", 50], 473, 898, [278, null]],
+[65, ["string", "Boston"], 285, 982, [281, null]],
+[66, ["journal", "./samples/images/Boston.png"], 936, 698, [72, null]],
+[67, "setscale", 878, 530, [362, 68, 69]],
+[68, ["number", 100], 955, 530, [67, null]],
+[69, ["setxy2", 0], 878, 572, [67, 70, 71, 363]],
+[70, ["number", 0], 936, 572, [69, null]],
+[71, ["number", 0], 936, 614, [69, null]],
+[72, "show", 878, 698, [363, 66, 360]],
+[73, ["journal", "./samples/images/earth.png"], 285, 666, [231, null]],
+[74, "hat", 860, 200, [null, 75, 336]],
+[75, ["string", "setup"], 918, 212, [74, null]],
+[76, ["storein", 0], 878, 288, [336, 77, 78, 323]],
+[77, ["string", "scalex"], 945, 288, [76, null]],
+[78, ["division2", 0], 945, 330, [76, 322, 79]],
+[79, ["number", 1200], 1039, 372, [78, null]],
+[80, ["storein", 0], 878, 540, [328, 81, 82, 321]],
+[81, ["string", "box"], 945, 540, [80, null]],
+[82, ["product2", 0], 945, 582, [80, 83, 84]],
+[83, ["number", -260], 999, 582, [82, null]],
+[84, "box", 999, 624, [82, 85, null]],
+[85, ["string", "scalex"], 1053, 624, [84, null]],
+[86, "hat", 860, 400, [null, 87, 332]],
+[87, ["string", "game over"], 918, 412, [86, null]],
+[88, "setpensize", 878, 782, [360, 89, 354]],
+[89, ["number", 15], 980, 782, [88, null]],
+[90, ["setxy2", 0], 878, 866, [354, 91, 92, 355]],
+[91, ["number", 0], 936, 866, [90, null]],
+[92, ["number", 0], 936, 908, [90, null]],
+[93, ["repeat", 42], 878, 992, [355, 94, 95, null]],
+[94, ["number", 5], 936, 992, [93, null]],
+[95, "setcolor", 896, 1034, [93, 96, 97]],
+[96, "heading", 973, 1034, [95, null]],
+[97, "forward", 896, 1076, [95, 98, 99]],
+[98, ["number", 200], 966, 1076, [97, null]],
+[99, "right", 896, 1118, [97, 100, null]],
+[100, ["number", 144], 954, 1118, [99, null]],
+[101, "clean", 878, 666, [321, 315]],
+[102, ["number", -200], 473, 430, [240, null]],
+[103, ["number", -220], 473, 514, [246, null]],
+[104, ["number", 200], 473, 598, [252, null]],
+[105, ["string", "USA"], 285, 682, [255, null]],
+[106, ["journal", "./samples/images/United_States.png"], 285, 866, [18, null]],
+[107, "hat", 640, 200, [null, 108, 337]],
+[108, ["string", "go"], 698, 212, [107, null]],
+[109, ["if", 0], 658, 584, [287, 110, 347, 350]],
+[110, ["greater2", 0], 714, 550, [109, 111, 113, null]],
+[111, "box", 770, 550, [110, 112, null]],
+[112, ["string", "test"], 824, 550, [111, null]],
+[113, ["number", 0], 794, 592, [110, null]],
+[114, ["setxy2", 20.0], 658, 430, [343, 115, 118, 344]],
+[115, ["minus2", 0], 716, 430, [114, 116, 177]],
+[116, "box", 789, 430, [115, 117, null]],
+[117, ["string", "x"], 843, 430, [116, null]],
+[118, "box", 716, 512, [114, 119, null]],
+[119, ["string", "y"], 770, 512, [118, null]],
+[120, ["arc", 0], 658, 596, [344, 121, 122, 179]],
+[121, ["number", 360], 716, 596, [120, null]],
+[122, "box", 716, 638, [120, 314, null]],
+[123, ["setxy2", 0], 658, 752, [350, 124, 125, 351]],
+[124, ["number", 0], 716, 752, [123, null]],
+[125, "box", 716, 794, [123, 126, null]],
+[126, ["string", "box"], 770, 794, [125, null]],
+[127, "show", 658, 962, [356, 128, 195]],
+[128, ["plus2", 0], 716, 962, [127, 129, 130]],
+[129, ["string", "Put the turtle in "], 770, 962, [128, null]],
+[130, "box", 770, 1004, [128, 131, null]],
+[131, ["string", "name"], 824, 1004, [130, null]],
+[132, ["setxy2", 0], 658, 1088, [352, 133, 134, 353]],
+[133, ["number", 0], 716, 1088, [132, null]],
+[134, ["number", 0], 716, 1130, [132, null]],
+[135, ["forever", 105], 658, 488, [368, 136, null]],
+[136, "stack", 676, 522, [135, 137, 194]],
+[137, ["string", "calculate distance"], 734, 522, [136, null]],
+[138, ["if", 0], 676, 606, [194, 139, 156, 157]],
+[139, ["less2", 0], 732, 572, [138, 140, 142, null]],
+[140, "box", 788, 572, [139, 141, null]],
+[141, ["string", "distance"], 842, 572, [140, null]],
+[142, ["product2", 0], 812, 614, [139, 143, 145]],
+[143, "box", 866, 614, [142, 144, null]],
+[144, ["string", "size"], 920, 614, [143, null]],
+[145, "box", 866, 656, [142, 146, null]],
+[146, ["string", "scalex"], 920, 656, [145, null]],
+[147, "clean", 658, 288, [337, 148]],
+[148, ["setxy2", 0], 658, 330, [147, 149, 150, 151]],
+[149, ["number", 0], 716, 330, [148, null]],
+[150, ["number", 0], 716, 372, [148, null]],
+[151, "setscale", 658, 414, [148, 152, 358]],
+[152, ["number", 100], 735, 414, [151, null]],
+[153, "show", 658, 498, [358, 154, 287]],
+[154, "box", 716, 498, [153, 155, null]],
+[155, ["string", "picture"], 770, 498, [154, null]],
+[156, "stopstack", 694, 672, [138, null]],
+[157, "wait", 676, 732, [138, 158, null]],
+[158, ["number", 1], 734, 732, [157, null]],
+[159, ["start", 2.0], 400, 200, [null, 160]],
+[160, ["storein", 0], 400, 246, [159, 161, 162, 163]],
+[161, ["string", "test"], 467, 246, [160, null]],
+[162, ["number", 0.0], 467, 288, [160, null]],
+[163, "stack", 400, 330, [160, 164, 165]],
+[164, ["string", "setup"], 458, 330, [163, null]],
+[165, "stack", 400, 372, [163, 166, 167]],
+[166, ["string", "Q1"], 458, 372, [165, null]],
+[167, "stack", 400, 414, [165, 168, 169]],
+[168, ["string", "Q2"], 458, 414, [167, null]],
+[169, "stack", 400, 456, [167, 170, 171]],
+[170, ["string", "Q3"], 458, 456, [169, null]],
+[171, "stack", 400, 498, [169, 172, 173]],
+[172, ["string", "Q4"], 458, 498, [171, null]],
+[173, "stack", 400, 540, [171, 174, 175]],
+[174, ["string", "Q5"], 458, 540, [173, null]],
+[175, "stack", 400, 582, [173, 176, null]],
+[176, ["string", "game over"], 458, 582, [175, null]],
+[177, "box", 813, 472, [115, 178, null]],
+[178, ["string", "size"], 867, 472, [177, null]],
+[179, ["storein", 0], 658, 680, [120, 180, 181, 196]],
+[180, ["string", "box"], 725, 680, [179, null]],
+[181, ["plus2", 0], 725, 722, [179, 182, 183]],
+[182, "xcor", 779, 722, [181, null]],
+[183, "box", 779, 764, [181, 184, null]],
+[184, ["string", "size"], 833, 764, [183, null]],
+[185, "print", 658, 806, [196, 186, null]],
+[186, ["plus2", 0], 716, 806, [185, 187, 189]],
+[187, "box", 770, 806, [186, 188, null]],
+[188, ["string", "box"], 824, 806, [187, null]],
+[189, ["plus2", 0], 770, 848, [186, 190, 191]],
+[190, ["string", ", "], 824, 848, [189, null]],
+[191, "ycor", 824, 890, [189, null]],
+[192, ["number", -170], 473, 330, [214, null]],
+[193, ["vspace", 1], 843, 780, [48, null]],
+[194, ["vspace", 0], 676, 564, [136, 138]],
+[195, ["vspace", 0], 658, 1004, [127, 352]],
+[196, ["vspace", 0], 658, 764, [179, 185]],
+[197, ["product2", 0], 419, 530, [200, 8, 198]],
+[198, "box", 473, 572, [197, 199, null]],
+[199, ["string", "scalex"], 527, 572, [198, null]],
+[200, ["identity2", 40.0], 285, 530, [6, 197]],
+[201, ["product2", 0], 419, 614, [202, 11, 203]],
+[202, ["identity2", 40.0], 285, 614, [9, 201]],
+[203, "box", 473, 656, [201, 204, null]],
+[204, ["string", "scaley"], 527, 656, [203, null]],
+[205, "box", 473, 740, [207, 206, null]],
+[206, ["string", "scalex"], 527, 740, [205, null]],
+[207, ["product2", 0], 419, 698, [208, 14, 205]],
+[208, ["identity2", 40.0], 285, 698, [12, 207]],
+[209, "hat", 200, 200, [null, 210, 342]],
+[210, ["string", "Q1"], 258, 212, [209, null]],
+[211, ["storein", 0], 218, 288, [342, 212, 213, 217]],
+[212, ["string", "x"], 285, 288, [211, null]],
+[213, ["identity2", 40.0], 285, 330, [211, 214]],
+[214, ["product2", 0], 419, 330, [213, 192, 215]],
+[215, "box", 473, 372, [214, 216, null]],
+[216, ["string", "scalex"], 527, 372, [215, null]],
+[217, ["storein", 0], 218, 372, [211, 218, 219, 223]],
+[218, ["string", "y"], 285, 372, [217, null]],
+[219, ["identity2", 40.0], 285, 414, [217, 220]],
+[220, ["product2", 0], 419, 414, [219, 3, 221]],
+[221, "box", 473, 456, [220, 222, null]],
+[222, ["string", "scaley"], 527, 456, [221, null]],
+[223, ["storein", 0], 218, 456, [217, 224, 225, 229]],
+[224, ["string", "size"], 285, 456, [223, null]],
+[225, ["identity2", 40.0], 285, 498, [223, 226]],
+[226, ["product2", 0], 419, 498, [225, 2, 227]],
+[227, "box", 473, 540, [226, 228, null]],
+[228, ["string", "scalex"], 527, 540, [227, null]],
+[229, ["storein", 0], 218, 540, [223, 230, 0, 231]],
+[230, ["string", "name"], 285, 540, [229, null]],
+[231, ["storein", 0], 218, 624, [229, 232, 73, 233]],
+[232, ["string", "picture"], 285, 624, [231, null]],
+[233, "stack", 218, 708, [231, 234, null]],
+[234, ["string", "go"], 276, 708, [233, null]],
+[235, "hat", 200, 300, [null, 236, 341]],
+[236, ["string", "Q2"], 258, 312, [235, null]],
+[237, ["storein", 0], 218, 388, [341, 238, 239, 243]],
+[238, ["string", "x"], 285, 388, [237, null]],
+[239, ["identity2", 40.0], 285, 430, [237, 240]],
+[240, ["product2", 0], 419, 430, [239, 102, 241]],
+[241, "box", 473, 472, [240, 242, null]],
+[242, ["string", "scalex"], 527, 472, [241, null]],
+[243, ["storein", 0], 218, 472, [237, 244, 245, 249]],
+[244, ["string", "y"], 285, 472, [243, null]],
+[245, ["identity2", 40.0], 285, 514, [243, 246]],
+[246, ["product2", 0], 419, 514, [245, 103, 247]],
+[247, "box", 473, 556, [246, 248, null]],
+[248, ["string", "scaley"], 527, 556, [247, null]],
+[249, ["storein", 0], 218, 556, [243, 250, 251, 255]],
+[250, ["string", "size"], 285, 556, [249, null]],
+[251, ["identity2", 40.0], 285, 598, [249, 252]],
+[252, ["product2", 0], 419, 598, [251, 104, 253]],
+[253, "box", 473, 640, [252, 254, null]],
+[254, ["string", "scalex"], 527, 640, [253, null]],
+[255, ["storein", 0], 218, 640, [249, 256, 105, 257]],
+[256, ["string", "name"], 285, 640, [255, null]],
+[257, ["storein", 0], 218, 724, [255, 258, 1, 259]],
+[258, ["string", "picture"], 285, 724, [257, null]],
+[259, "stack", 218, 808, [257, 260, null]],
+[260, ["string", "go"], 276, 808, [259, null]],
+[261, "hat", 200, 600, [null, 262, 338]],
+[262, ["string", "Q5"], 258, 612, [261, null]],
+[263, ["storein", 0], 218, 688, [338, 264, 265, 269]],
+[264, ["string", "x"], 285, 688, [263, null]],
+[265, ["identity2", 40.0], 285, 730, [263, 266]],
+[266, ["product2", 0], 419, 730, [265, 62, 267]],
+[267, "box", 473, 772, [266, 268, null]],
+[268, ["string", "scalex"], 527, 772, [267, null]],
+[269, ["storein", 0], 218, 772, [263, 270, 271, 275]],
+[270, ["string", "y"], 285, 772, [269, null]],
+[271, ["identity2", 40.0], 285, 814, [269, 272]],
+[272, ["product2", 0], 419, 814, [271, 63, 273]],
+[273, "box", 473, 856, [272, 274, null]],
+[274, ["string", "scaley"], 527, 856, [273, null]],
+[275, ["storein", 0], 218, 856, [269, 276, 277, 281]],
+[276, ["string", "size"], 285, 856, [275, null]],
+[277, ["identity2", 40.0], 285, 898, [275, 278]],
+[278, ["product2", 0], 419, 898, [277, 64, 279]],
+[279, "box", 473, 940, [278, 280, null]],
+[280, ["string", "scalex"], 527, 940, [279, null]],
+[281, ["storein", 0], 218, 940, [275, 282, 65, 283]],
+[282, ["string", "name"], 285, 940, [281, null]],
+[283, ["storein", 0], 218, 1024, [281, 284, 27, 285]],
+[284, ["string", "picture"], 285, 1024, [283, null]],
+[285, "stack", 218, 1108, [283, 286, null]],
+[286, ["string", "go"], 276, 1108, [285, null]],
+[287, ["vspace", 1], 658, 540, [153, 109]],
+[288, "hat", 200, 500, [null, 289, 339]],
+[289, ["string", "Q4"], 258, 512, [288, null]],
+[290, ["storein", 0], 218, 588, [339, 291, 292, 296]],
+[291, ["string", "x"], 285, 588, [290, null]],
+[292, ["identity2", 40.0], 285, 630, [290, 293]],
+[293, ["product2", 0], 419, 630, [292, 23, 294]],
+[294, "box", 473, 672, [293, 295, null]],
+[295, ["string", "scalex"], 527, 672, [294, null]],
+[296, ["storein", 0], 218, 672, [290, 297, 298, 302]],
+[297, ["string", "y"], 285, 672, [296, null]],
+[298, ["identity2", 40.0], 285, 714, [296, 299]],
+[299, ["product2", 0], 419, 714, [298, 24, 300]],
+[300, "box", 473, 756, [299, 301, null]],
+[301, ["string", "scaley"], 527, 756, [300, null]],
+[302, ["storein", 0], 218, 756, [296, 303, 304, 308]],
+[303, ["string", "size"], 285, 756, [302, null]],
+[304, ["identity2", 40.0], 285, 798, [302, 305]],
+[305, ["product2", 0], 419, 798, [304, 25, 306]],
+[306, "box", 473, 840, [305, 307, null]],
+[307, ["string", "scalex"], 527, 840, [306, null]],
+[308, ["storein", 0], 218, 840, [302, 309, 26, 310]],
+[309, ["string", "name"], 285, 840, [308, null]],
+[310, ["storein", 0], 218, 924, [308, 311, 20, 312]],
+[311, ["string", "picture"], 285, 924, [310, null]],
+[312, "stack", 218, 1008, [310, 313, null]],
+[313, ["string", "go"], 276, 1008, [312, null]],
+[314, ["string", "size"], 770, 638, [122, null]],
+[315, ["if", 0], 878, 708, [101, 316, 318, null]],
+[316, ["equal2", 0], 934, 674, [315, 319, 317, null]],
+[317, ["number", 0], 990, 716, [316, null]],
+[318, "hideblocks", 896, 774, [315, null]],
+[319, "box", 990, 674, [316, 320, null]],
+[320, ["string", "test"], 1044, 674, [319, null]],
+[321, ["vspace", 0], 878, 624, [80, 101]],
+[322, "width", 1015, 330, [78, null]],
+[323, ["vspace", 0], 878, 372, [76, 324]],
+[324, ["storein", 0], 878, 414, [323, 325, 326, 328]],
+[325, ["string", "scaley"], 945, 414, [324, null]],
+[326, ["division2", 0], 945, 456, [324, 329, 327]],
+[327, ["number", 900], 1039, 498, [326, null]],
+[328, ["vspace", 0], 878, 498, [324, 80]],
+[329, "height", 1015, 456, [326, null]],
+[330, "setscale", 658, 878, [351, 331, 356]],
+[331, ["number", 50.0], 735, 878, [330, null]],
+[332, "sandwichclampcollapsed", 860, 454, [86, 362, null]],
+[333, ["vspace", 20], 843, 614, [39, 48]],
+[334, ["vspace", 20], 843, 448, [30, 39]],
+[335, "sandwichclampcollapsed", 860, 354, [28, 30, null]],
+[336, "sandwichclampcollapsed", 860, 254, [74, 76, null]],
+[337, "sandwichclampcollapsed", 640, 254, [107, 147, null]],
+[338, "sandwichclampcollapsed", 200, 654, [261, 263, null]],
+[339, "sandwichclampcollapsed", 200, 554, [288, 290, null]],
+[340, "sandwichclampcollapsed", 200, 454, [4, 6, null]],
+[341, "sandwichclampcollapsed", 200, 354, [235, 237, null]],
+[342, "sandwichclampcollapsed", 200, 254, [209, 211, null]],
+[343, "penup", 658, 388, [349, 114]],
+[344, "pendown", 658, 554, [114, 120]],
+[345, "hat", 640, 300, [null, 346, 349]],
+[346, ["string", "test"], 698, 312, [345, null]],
+[347, "stack", 676, 650, [109, 348, null]],
+[348, ["string", "test"], 734, 650, [347, null]],
+[349, "sandwichclampcollapsed", 640, 354, [345, 343, null]],
+[350, "penup", 658, 710, [109, 123]],
+[351, "pendown", 658, 836, [123, 330]],
+[352, "penup", 658, 1046, [195, 132]],
+[353, "pendown", 658, 1172, [132, 366]],
+[354, "penup", 878, 824, [88, 90]],
+[355, "pendown", 878, 950, [90, 93]],
+[356, "seth", 658, 920, [330, 357, 127]],
+[357, ["number", 0], 716, 920, [356, null]],
+[358, "seth", 658, 456, [151, 359, 153]],
+[359, ["number", 0], 716, 456, [358, null]],
+[360, "wait", 878, 740, [72, 361, 88]],
+[361, ["number", 1], 936, 740, [360, null]],
+[362, "penup", 878, 488, [332, 67]],
+[363, "pendown", 878, 656, [69, 72]],
+[364, "hat", 640, 400, [null, 365, 368]],
+[365, ["string", "loop"], 698, 412, [364, null]],
+[366, "stack", 658, 1214, [353, 367, null]],
+[367, ["string", "loop"], 716, 1214, [366, null]],
+[368, "sandwichclampcollapsed", 640, 454, [364, 135, null]],
+[369, "setscale", 459, 23, [null, 370, null]],
+[370, ["number", 33], 536, 23, [369, null]]]
diff --git a/samples/game-shapes-and-color.ta b/samples/game-shapes-and-color.ta
index 952bd9c..ec9b550 100644
--- a/samples/game-shapes-and-color.ta
+++ b/samples/game-shapes-and-color.ta
@@ -1,392 +1,391 @@
-[[0, "hat", 801, 239, [null, 1, 380]],
-[1, ["string", "circle"], 859, 247, [0, null]],
-[2, "penup", 801, 323, [380, 3]],
-[3, "seth", 801, 365, [2, 4, 5]],
-[4, ["number", 270], 900, 365, [3, null]],
-[5, "forward", 801, 407, [3, 6, 7]],
-[6, ["number", 56], 872, 407, [5, null]],
-[7, "seth", 801, 449, [5, 8, 9]],
-[8, ["number", 0], 900, 449, [7, null]],
-[9, "pendown", 801, 491, [7, 10]],
-[10, ["arc", 0], 801, 533, [9, 11, 12, 335]],
-[11, ["number", 360], 859, 533, [10, null]],
-[12, ["number", 56], 859, 575, [10, null]],
-[13, "hat", 796, 97, [null, 14, 378]],
-[14, ["string", "square"], 854, 105, [13, null]],
-[15, "penup", 796, 181, [378, 16]],
-[16, "seth", 796, 223, [15, 17, 18]],
-[17, ["number", 0], 895, 223, [16, null]],
-[18, "back", 796, 265, [16, 19, 20]],
-[19, ["number", 50], 854, 265, [18, null]],
-[20, "seth", 796, 307, [18, 21, 22]],
-[21, ["number", 270], 895, 307, [20, null]],
-[22, "forward", 796, 349, [20, 23, 24]],
-[23, ["number", 50], 867, 349, [22, null]],
-[24, "seth", 796, 391, [22, 25, 26]],
-[25, ["number", 0], 895, 391, [24, null]],
-[26, "pendown", 796, 433, [24, 27]],
-[27, "repeat", 796, 475, [26, 28, 29, 350]],
-[28, ["number", 4], 847, 475, [27, null]],
-[29, "forward", 861, 535, [27, 30, 31]],
-[30, ["number", 100], 932, 535, [29, null]],
-[31, "right", 861, 577, [29, 32, null]],
-[32, ["number", 90], 919, 577, [31, null]],
-[33, "hat", 805, 532, [null, 34, 379]],
-[34, ["string", "triangle"], 863, 540, [33, null]],
-[35, "penup", 805, 616, [379, 36]],
-[36, "seth", 805, 658, [35, 37, 38]],
-[37, ["number", 270], 904, 658, [36, null]],
-[38, "forward", 805, 700, [36, 39, 40]],
-[39, ["number", 60], 876, 700, [38, null]],
-[40, "seth", 805, 742, [38, 41, 42]],
-[41, ["number", 0], 904, 742, [40, null]],
-[42, "back", 805, 784, [40, 43, 44]],
-[43, ["number", 50], 863, 784, [42, null]],
-[44, "seth", 805, 826, [42, 45, 46]],
-[45, ["number", 30], 904, 826, [44, null]],
-[46, "pendown", 805, 868, [44, 47]],
-[47, "repeat", 805, 910, [46, 48, 49, 349]],
-[48, ["number", 3], 856, 910, [47, null]],
-[49, "forward", 870, 970, [47, 50, 51]],
-[50, ["number", 120], 941, 970, [49, null]],
-[51, "right", 870, 1012, [49, 52, null]],
-[52, ["number", 120], 928, 1012, [51, null]],
-[53, "seth", 805, 1070, [349, 54, 348]],
-[54, ["number", 0], 904, 1070, [53, null]],
-[55, "hat", 806, 387, [null, 56, 381]],
-[56, ["string", "star"], 864, 395, [55, null]],
-[57, "penup", 806, 471, [381, 58]],
-[58, "seth", 806, 513, [57, 59, 60]],
-[59, ["number", 0], 905, 513, [58, null]],
-[60, "back", 806, 555, [58, 61, 62]],
-[61, ["number", 44], 864, 555, [60, null]],
-[62, "seth", 806, 597, [60, 63, 64]],
-[63, ["number", 270], 905, 597, [62, null]],
-[64, "forward", 806, 639, [62, 65, 66]],
-[65, ["number", 30], 877, 639, [64, null]],
-[66, "seth", 806, 681, [64, 67, 68]],
-[67, ["number", 18], 905, 681, [66, null]],
-[68, "pendown", 806, 723, [66, 69]],
-[69, "repeat", 806, 765, [68, 70, 71, 346]],
-[70, ["number", 5], 857, 765, [69, null]],
-[71, "forward", 871, 825, [69, 72, 73]],
-[72, ["number", 100], 942, 825, [71, null]],
-[73, "right", 871, 867, [71, 74, null]],
-[74, ["number", 144], 929, 867, [73, null]],
-[75, "seth", 806, 925, [346, 76, 347]],
-[76, ["number", 0], 905, 925, [75, null]],
-[77, "hat", 1043, 420, [null, 78, 370]],
-[78, ["string", "shapes"], 1101, 428, [77, null]],
-[79, "setpensize", 1043, 504, [370, 80, 356]],
-[80, ["number", 10], 1145, 504, [79, null]],
-[81, ["setxy2", 0], 1043, 588, [356, 82, 83, 357]],
-[82, ["number", -100], 1101, 588, [81, null]],
-[83, ["number", 100], 1101, 630, [81, null]],
-[84, "setcolor", 1043, 714, [357, 85, 86]],
-[85, "pop", 1120, 714, [84, null]],
-[86, "stack", 1043, 756, [84, 87, 358]],
-[87, "pop", 1101, 756, [86, null]],
-[88, ["setxy2", 0], 1043, 840, [358, 89, 90, 359]],
-[89, ["number", 100], 1101, 840, [88, null]],
-[90, ["number", 100], 1101, 882, [88, null]],
-[91, "setcolor", 1043, 966, [359, 92, 93]],
-[92, "pop", 1120, 966, [91, null]],
-[93, "stack", 1043, 1008, [91, 94, 360]],
-[94, "pop", 1101, 1008, [93, null]],
-[95, ["setxy2", 0], 1043, 1092, [360, 96, 97, 361]],
-[96, ["number", 100], 1101, 1092, [95, null]],
-[97, ["number", -100], 1101, 1134, [95, null]],
-[98, "setcolor", 1043, 1218, [361, 99, 100]],
-[99, "pop", 1120, 1218, [98, null]],
-[100, "stack", 1043, 1260, [98, 101, 362]],
-[101, "pop", 1101, 1260, [100, null]],
-[102, ["setxy2", 0], 1043, 1344, [362, 103, 104, 363]],
-[103, ["number", -100], 1101, 1344, [102, null]],
-[104, ["number", -100], 1101, 1386, [102, null]],
-[105, "setcolor", 1043, 1470, [363, 106, 107]],
-[106, "pop", 1120, 1470, [105, null]],
-[107, "stack", 1043, 1512, [105, 108, 364]],
-[108, "pop", 1101, 1512, [107, null]],
-[109, "hat", 1039, 263, [null, 110, 377]],
-[110, ["string", "calculate distance"], 1097, 271, [109, null]],
-[111, ["storein", 0], 1039, 347, [377, 112, 113, 355]],
-[112, ["string", "dx"], 1107, 347, [111, null]],
-[113, ["minus2", 0], 1107, 389, [111, 114, 116]],
-[114, "box", 1161, 389, [113, 115, null]],
-[115, ["string", "x"], 1216, 389, [114, null]],
-[116, "xcor", 1185, 431, [113, null]],
-[117, ["storein", 0], 1039, 473, [355, 118, 119, 354]],
-[118, ["string", "dy"], 1107, 473, [117, null]],
-[119, ["minus2", 0], 1107, 515, [117, 120, 122]],
-[120, "box", 1161, 515, [119, 121, null]],
-[121, ["string", "y"], 1216, 515, [120, null]],
-[122, "ycor", 1185, 557, [119, null]],
-[123, ["storein", 0], 1039, 599, [354, 124, 125, 334]],
-[124, ["string", "distance"], 1107, 599, [123, null]],
-[125, "sqrt", 1107, 641, [123, 126]],
-[126, ["plus2", 20.0], 1161, 641, [125, 127, 132]],
-[127, ["product2", 0], 1215, 641, [126, 128, 130]],
-[128, "box", 1269, 641, [127, 129, null]],
-[129, ["string", "dx"], 1324, 641, [128, null]],
-[130, "box", 1269, 683, [127, 131, null]],
-[131, ["string", "dx"], 1324, 683, [130, null]],
-[132, ["product2", 0], 1215, 723, [126, 133, 135]],
-[133, "box", 1269, 723, [132, 134, null]],
-[134, ["string", "dy"], 1324, 723, [133, null]],
-[135, "box", 1269, 765, [132, 136, null]],
-[136, ["string", "dy"], 1324, 765, [135, null]],
-[137, "hat", 516, 195, [null, 138, 382]],
-[138, ["string", "q1"], 574, 203, [137, null]],
-[139, ["storein", 0], 516, 279, [382, 140, 141, 142]],
-[140, ["string", "x"], 584, 279, [139, null]],
-[141, ["number", 100], 584, 321, [139, null]],
-[142, ["storein", 0], 516, 363, [139, 143, 144, 145]],
-[143, ["string", "y"], 584, 363, [142, null]],
-[144, ["number", 100], 584, 405, [142, null]],
-[145, "push", 516, 447, [142, 158, 146]],
-[146, "stack", 516, 489, [145, 147, 351]],
-[147, ["string", "ask"], 574, 489, [146, null]],
-[148, "hat", 518, 335, [null, 156, 383]],
-[149, ["storein", 0], 518, 419, [383, 150, 160, 151]],
-[150, ["string", "x"], 586, 419, [149, null]],
-[151, ["storein", 0], 518, 503, [149, 152, 159, 153]],
-[152, ["string", "y"], 586, 503, [151, null]],
-[153, "push", 518, 587, [151, 157, 154]],
-[154, "stack", 518, 629, [153, 155, 352]],
-[155, ["string", "ask"], 576, 629, [154, null]],
-[156, ["string", "q2"], 576, 343, [148, null]],
-[157, ["string", "Put the turtle in the red square."], 576, 587, [153, null]],
-[158, ["string", "Put the turtle in the blue triangle."], 574, 447, [145, null]],
-[159, ["number", 100], 586, 545, [151, null]],
-[160, ["number", -100], 586, 461, [149, null]],
-[161, "hat", 513, 477, [null, 162, 384]],
-[162, ["string", "q3"], 571, 485, [161, null]],
-[163, ["storein", 0], 513, 561, [384, 164, 183, 165]],
-[164, ["string", "x"], 581, 561, [163, null]],
-[165, ["storein", 0], 513, 645, [163, 166, 167, 168]],
-[166, ["string", "y"], 581, 645, [165, null]],
-[167, ["number", -100], 581, 687, [165, null]],
-[168, "push", 513, 729, [165, 182, 169]],
-[169, "stack", 513, 771, [168, 170, 353]],
-[170, ["string", "ask"], 571, 771, [169, null]],
-[171, "hat", 1052, 102, [null, 172, 385]],
-[172, ["string", "fini"], 1110, 110, [171, null]],
-[173, ["storein", 0], 1052, 186, [385, 176, 174, 175]],
-[174, ["number", 0], 1120, 228, [173, null]],
-[175, ["storein", 0], 1052, 270, [173, 177, 178, 179]],
-[176, ["string", "x"], 1120, 186, [173, null]],
-[177, ["string", "y"], 1120, 270, [175, null]],
-[178, ["number", 0], 1120, 312, [175, null]],
-[179, "push", 1052, 354, [175, 184, 180]],
-[180, "stack", 1052, 396, [179, 181, 345]],
-[181, ["string", "ask"], 1110, 396, [180, null]],
-[182, ["string", "Put the turtle in the red circle."], 571, 729, [168, null]],
-[183, ["number", -100], 581, 603, [163, null]],
-[184, ["string", "Well played!"], 1110, 354, [179, null]],
-[185, ["start", 2.0], 345, 201, [null, 374]],
-[186, ["storein", 0], 345, 277, [374, 187, 188, 189]],
-[187, ["string", "blue"], 413, 277, [186, null]],
-[188, ["number", 70], 413, 319, [186, null]],
-[189, ["storein", 0], 345, 361, [186, 190, 191, 192]],
-[190, ["string", "red"], 413, 361, [189, null]],
-[191, ["number", 0], 413, 403, [189, null]],
-[192, ["storein", 0], 345, 445, [189, 193, 194, 195]],
-[193, ["string", "green"], 413, 445, [192, null]],
-[194, ["number", 30], 413, 487, [192, null]],
-[195, "hideblocks", 345, 529, [192, 196]],
-[196, "stack", 345, 571, [195, 197, 198]],
-[197, ["string", "pattern a"], 403, 571, [196, null]],
-[198, "stack", 345, 613, [196, 199, 200]],
-[199, ["string", "q1"], 403, 613, [198, null]],
-[200, "stack", 345, 655, [198, 228, 201]],
-[201, "stack", 345, 697, [200, 202, 203]],
-[202, ["string", "q2"], 403, 697, [201, null]],
-[203, "stack", 345, 739, [201, 227, 204]],
-[204, "stack", 345, 781, [203, 205, 206]],
-[205, ["string", "q3"], 403, 781, [204, null]],
-[206, "stack", 345, 823, [204, 207, 343]],
-[207, ["string", "fini"], 403, 823, [206, null]],
-[208, "hat", 290, 493, [null, 209, 376]],
-[209, ["string", "answer"], 348, 501, [208, null]],
-[210, ["setxy2", 0], 290, 619, [388, 211, 212, 213]],
-[211, ["number", 0], 348, 619, [210, null]],
-[212, ["number", 0], 348, 661, [210, null]],
-[213, "forever", 290, 703, [210, 214, 336]],
-[214, "stack", 351, 721, [213, 215, 337]],
-[215, ["string", "calculate distance"], 409, 721, [214, null]],
-[216, "if", 351, 805, [337, 217, 221, 338]],
-[217, ["less2", 0], 389, 771, [216, 218, 220, null]],
-[218, "box", 435, 771, [217, 219, null]],
-[219, ["string", "distance"], 490, 771, [218, null]],
-[220, ["number", 100], 435, 813, [217, null]],
-[221, ["setxy2", 0], 403, 873, [216, 222, 223, 389]],
-[222, ["number", 0], 461, 873, [221, null]],
-[223, ["number", 0], 461, 915, [221, null]],
-[224, "stopstack", 403, 999, [389, null]],
-[225, "wait", 351, 1053, [338, 226, null]],
-[226, ["number", 1], 409, 1053, [225, null]],
-[227, ["string", "pattern c"], 403, 739, [203, null]],
-[228, ["string", "pattern b"], 403, 655, [200, null]],
-[229, "hat", 1313, 430, [null, 230, 373]],
-[230, ["string", "pattern a"], 1371, 438, [229, null]],
-[231, "clean", 1313, 514, [373, 232]],
-[232, "push", 1313, 556, [231, 233, 234]],
-[233, ["string", "square"], 1371, 556, [232, null]],
-[234, "push", 1313, 598, [232, 235, 237]],
-[235, "box", 1371, 598, [234, 236, null]],
-[236, ["string", "blue"], 1426, 598, [235, null]],
-[237, "push", 1313, 640, [234, 238, 239]],
-[238, ["string", "circle"], 1371, 640, [237, null]],
-[239, "push", 1313, 682, [237, 240, 242]],
-[240, "box", 1371, 682, [239, 241, null]],
-[241, ["string", "green"], 1426, 682, [240, null]],
-[242, "push", 1313, 724, [239, 243, 244]],
-[243, ["string", "triangle"], 1371, 724, [242, null]],
-[244, "push", 1313, 766, [242, 245, 247]],
-[245, "box", 1371, 766, [244, 246, null]],
-[246, ["string", "blue"], 1426, 766, [245, null]],
-[247, "push", 1313, 808, [244, 248, 249]],
-[248, ["string", "square"], 1371, 808, [247, null]],
-[249, "push", 1313, 850, [247, 250, 252]],
-[250, "box", 1371, 850, [249, 251, null]],
-[251, ["string", "red"], 1426, 850, [250, null]],
-[252, "stack", 1313, 892, [249, 253, 342]],
-[253, ["string", "shapes"], 1371, 892, [252, null]],
-[254, "hat", 1306, 268, [null, 255, 372]],
-[255, ["string", "pattern b"], 1364, 276, [254, null]],
-[256, "clean", 1306, 352, [372, 257]],
-[257, "push", 1306, 394, [256, 258, 259]],
-[258, ["string", "triangle"], 1364, 394, [257, null]],
-[259, "push", 1306, 436, [257, 260, 262]],
-[260, "box", 1364, 436, [259, 261, null]],
-[261, ["string", "red"], 1419, 436, [260, null]],
-[262, "push", 1306, 478, [259, 263, 264]],
-[263, ["string", "circle"], 1364, 478, [262, null]],
-[264, "push", 1306, 520, [262, 265, 267]],
-[265, "box", 1364, 520, [264, 266, null]],
-[266, ["string", "green"], 1419, 520, [265, null]],
-[267, "push", 1306, 562, [264, 268, 269]],
-[268, ["string", "square"], 1364, 562, [267, null]],
-[269, "push", 1306, 604, [267, 270, 272]],
-[270, "box", 1364, 604, [269, 271, null]],
-[271, ["string", "green"], 1419, 604, [270, null]],
-[272, "push", 1306, 646, [269, 273, 274]],
-[273, ["string", "square"], 1364, 646, [272, null]],
-[274, "push", 1306, 688, [272, 275, 277]],
-[275, "box", 1364, 688, [274, 276, null]],
-[276, ["string", "red"], 1419, 688, [275, null]],
-[277, "stack", 1306, 730, [274, 278, 341]],
-[278, ["string", "shapes"], 1364, 730, [277, null]],
-[279, "hat", 1296, 108, [null, 280, 371]],
-[280, ["string", "pattern c"], 1354, 116, [279, null]],
-[281, "clean", 1296, 192, [371, 282]],
-[282, "push", 1296, 234, [281, 283, 284]],
-[283, ["string", "circle"], 1354, 234, [282, null]],
-[284, "push", 1296, 276, [282, 285, 287]],
-[285, "box", 1354, 276, [284, 286, null]],
-[286, ["string", "red"], 1409, 276, [285, null]],
-[287, "push", 1296, 318, [284, 288, 289]],
-[288, ["string", "circle"], 1354, 318, [287, null]],
-[289, "push", 1296, 360, [287, 290, 292]],
-[290, "box", 1354, 360, [289, 291, null]],
-[291, ["string", "green"], 1409, 360, [290, null]],
-[292, "push", 1296, 402, [289, 293, 294]],
-[293, ["string", "square"], 1354, 402, [292, null]],
-[294, "push", 1296, 444, [292, 295, 297]],
-[295, "box", 1354, 444, [294, 296, null]],
-[296, ["string", "blue"], 1409, 444, [295, null]],
-[297, "push", 1296, 486, [294, 298, 299]],
-[298, ["string", "triangle"], 1354, 486, [297, null]],
-[299, "push", 1296, 528, [297, 300, 302]],
-[300, "box", 1354, 528, [299, 301, null]],
-[301, ["string", "red"], 1409, 528, [300, null]],
-[302, "stack", 1296, 570, [299, 303, 340]],
-[303, ["string", "shapes"], 1354, 570, [302, null]],
-[304, "hat", 284, 343, [null, 305, 375]],
-[305, ["string", "ask"], 342, 351, [304, null]],
-[306, ["setxy2", 0], 284, 469, [386, 307, 308, 387]],
-[307, ["number", -300], 342, 469, [306, null]],
-[308, ["number", 300], 342, 511, [306, null]],
-[309, "seth", 284, 595, [387, 310, 311]],
-[310, ["number", 90], 383, 595, [309, null]],
-[311, "setcolor", 284, 637, [309, 312, 313]],
-[312, ["number", 0], 361, 637, [311, null]],
-[313, "setshade", 284, 679, [311, 314, 315]],
-[314, ["number", 100], 369, 679, [313, null]],
-[315, "setpensize", 284, 721, [313, 316, 317]],
-[316, ["number", 120], 386, 721, [315, null]],
-[317, "forward", 284, 763, [315, 318, 319]],
-[318, ["number", 700], 355, 763, [317, null]],
-[319, "back", 284, 805, [317, 320, 321]],
-[320, ["number", 700], 342, 805, [319, null]],
-[321, "setshade", 284, 847, [319, 322, 323]],
-[322, ["number", 50], 369, 847, [321, null]],
-[323, "seth", 284, 889, [321, 324, 390]],
-[324, ["number", 0], 383, 889, [323, null]],
-[325, ["setxy2", 0], 284, 973, [390, 326, 327, 391]],
-[326, ["number", -300], 342, 973, [325, null]],
-[327, ["number", 300], 342, 1015, [325, null]],
-[328, "settextsize", 284, 1099, [391, 329, 330]],
-[329, ["number", 24], 388, 1099, [328, null]],
-[330, "show", 284, 1141, [328, 331, 332]],
-[331, "pop", 342, 1141, [330, null]],
-[332, "stack", 284, 1183, [330, 333, 339]],
-[333, ["string", "answer"], 342, 1183, [332, null]],
-[334, ["sandwichcollapsed", 1], 1039, 347, [123, null]],
-[335, ["sandwichcollapsed", 1], 801, 323, [10, null]],
-[336, ["sandwichcollapsed", 1], 290, 577, [213, null]],
-[337, ["vspace", 0], 351, 763, [214, 216]],
-[338, ["vspace", 60.0], 351, 891, [216, 225]],
-[339, ["sandwichcollapsed", 1], 284, 427, [332, null]],
-[340, ["sandwichcollapsed", 1], 1296, 192, [302, null]],
-[341, ["sandwichcollapsed", 1], 1306, 352, [277, null]],
-[342, ["sandwichcollapsed", 1], 1313, 514, [252, null]],
-[343, "showblocks", 345, 865, [206, 344]],
-[344, ["sandwichcollapsed", 1], 345, 277, [343, null]],
-[345, ["sandwichcollapsed", 1], 1052, 186, [180, null]],
-[346, ["vspace", 20.0], 806, 843, [69, 75]],
-[347, ["sandwichcollapsed", 1], 806, 471, [75, null]],
-[348, ["sandwichcollapsed", 1], 805, 616, [53, null]],
-[349, ["vspace", 20.0], 805, 988, [47, 53]],
-[350, ["sandwichcollapsed", 1], 796, 181, [27, null]],
-[351, ["sandwichcollapsed", 1], 516, 279, [146, null]],
-[352, ["sandwichcollapsed", 1], 518, 419, [154, null]],
-[353, ["sandwichcollapsed", 1], 513, 561, [169, null]],
-[354, ["vspace", 0], 1039, 557, [117, 123]],
-[355, ["vspace", 0], 1039, 431, [111, 117]],
-[356, "penup", 1043, 546, [79, 81]],
-[357, "pendown", 1043, 672, [81, 84]],
-[358, "penup", 1043, 798, [86, 88]],
-[359, "pendown", 1043, 924, [88, 91]],
-[360, "penup", 1043, 1050, [93, 95]],
-[361, "pendown", 1043, 1176, [95, 98]],
-[362, "penup", 1043, 1302, [100, 102]],
-[363, "pendown", 1043, 1428, [102, 105]],
-[364, "penup", 1043, 1554, [107, 366]],
-[365, "pendown", 1043, 1680, [366, 369]],
-[366, ["setxy2", 0], 1043, 1596, [364, 367, 368, 365]],
-[367, ["number", 0], 1101, 1596, [366, null]],
-[368, ["number", 0], 1101, 1638, [366, null]],
-[369, ["sandwichcollapsed", 1], 1043, 504, [365, null]],
-[370, "sandwichtop_no_arm_no_label", 1025, 470, [77, 79]],
-[371, "sandwichtop_no_arm_no_label", 1278, 158, [279, 281]],
-[372, "sandwichtop_no_arm_no_label", 1288, 318, [254, 256]],
-[373, "sandwichtop_no_arm_no_label", 1295, 480, [229, 231]],
-[374, "sandwichtop_no_arm_no_label", 327, 243, [185, 186]],
-[375, "sandwichtop_no_arm_no_label", 266, 393, [304, 386]],
-[376, "sandwichtop_no_arm_no_label", 272, 543, [208, 388]],
-[377, "sandwichtop_no_arm_no_label", 1021, 313, [109, 111]],
-[378, "sandwichtop_no_arm_no_label", 778, 147, [13, 15]],
-[379, "sandwichtop_no_arm_no_label", 787, 582, [33, 35]],
-[380, "sandwichtop_no_arm_no_label", 783, 289, [0, 2]],
-[381, "sandwichtop_no_arm_no_label", 788, 437, [55, 57]],
-[382, "sandwichtop_no_arm_no_label", 498, 245, [137, 139]],
-[383, "sandwichtop_no_arm_no_label", 500, 385, [148, 149]],
-[384, "sandwichtop_no_arm_no_label", 495, 527, [161, 163]],
-[385, "sandwichtop_no_arm_no_label", 1034, 152, [171, 173]],
-[386, "penup", 284, 427, [375, 306]],
-[387, "pendown", 284, 553, [306, 309]],
-[388, "penup", 290, 577, [376, 210]],
-[389, "pendown", 403, 957, [221, 224]],
-[390, "penup", 284, 931, [323, 325]],
-[391, "pendown", 284, 1057, [325, 328]]] \ No newline at end of file
+[[0, "hat", 801, 239, [null, 1, 377]],
+[1, ["string", "circle"], 859, 251, [0, null]],
+[2, "penup", 784, 303, [377, 3]],
+[3, "seth", 784, 345, [2, 4, 5]],
+[4, ["number", 270], 842, 345, [3, null]],
+[5, "forward", 784, 387, [3, 6, 7]],
+[6, ["number", 56], 854, 387, [5, null]],
+[7, "seth", 784, 429, [5, 8, 9]],
+[8, ["number", 0], 842, 429, [7, null]],
+[9, "pendown", 784, 471, [7, 10]],
+[10, ["arc", 0], 784, 513, [9, 11, 12, 333]],
+[11, ["number", 360], 842, 513, [10, null]],
+[12, ["number", 56], 842, 555, [10, null]],
+[13, "hat", 796, 97, [null, 14, 375]],
+[14, ["string", "square"], 854, 109, [13, null]],
+[15, "penup", 779, 161, [375, 16]],
+[16, "seth", 779, 203, [15, 17, 18]],
+[17, ["number", 0], 837, 203, [16, null]],
+[18, "back", 779, 245, [16, 19, 20]],
+[19, ["number", 50], 837, 245, [18, null]],
+[20, "seth", 779, 287, [18, 21, 22]],
+[21, ["number", 270], 837, 287, [20, null]],
+[22, "forward", 779, 329, [20, 23, 24]],
+[23, ["number", 50], 849, 329, [22, null]],
+[24, "seth", 779, 371, [22, 25, 26]],
+[25, ["number", 0], 837, 371, [24, null]],
+[26, "pendown", 779, 413, [24, 27]],
+[27, ["repeat", 21], 779, 455, [26, 28, 29, 347]],
+[28, ["number", 4], 837, 455, [27, null]],
+[29, "forward", 797, 497, [27, 30, 31]],
+[30, ["number", 100], 867, 497, [29, null]],
+[31, "right", 797, 539, [29, 32, null]],
+[32, ["number", 90], 855, 539, [31, null]],
+[33, "hat", 805, 532, [null, 34, 376]],
+[34, ["string", "triangle"], 863, 544, [33, null]],
+[35, "penup", 788, 596, [376, 36]],
+[36, "seth", 788, 638, [35, 37, 38]],
+[37, ["number", 270], 846, 638, [36, null]],
+[38, "forward", 788, 680, [36, 39, 40]],
+[39, ["number", 60], 858, 680, [38, null]],
+[40, "seth", 788, 722, [38, 41, 42]],
+[41, ["number", 0], 846, 722, [40, null]],
+[42, "back", 788, 764, [40, 43, 44]],
+[43, ["number", 50], 846, 764, [42, null]],
+[44, "seth", 788, 806, [42, 45, 46]],
+[45, ["number", 30], 846, 806, [44, null]],
+[46, "pendown", 788, 848, [44, 47]],
+[47, ["repeat", 21], 788, 890, [46, 48, 49, 346]],
+[48, ["number", 3], 846, 890, [47, null]],
+[49, "forward", 806, 932, [47, 50, 51]],
+[50, ["number", 120], 876, 932, [49, null]],
+[51, "right", 806, 974, [49, 52, null]],
+[52, ["number", 120], 864, 974, [51, null]],
+[53, "seth", 788, 1116, [346, 54, 345]],
+[54, ["number", 0], 846, 1116, [53, null]],
+[55, "hat", 806, 387, [null, 56, 378]],
+[56, ["string", "star"], 864, 399, [55, null]],
+[57, "penup", 789, 451, [378, 58]],
+[58, "seth", 789, 493, [57, 59, 60]],
+[59, ["number", 0], 847, 493, [58, null]],
+[60, "back", 789, 535, [58, 61, 62]],
+[61, ["number", 44], 847, 535, [60, null]],
+[62, "seth", 789, 577, [60, 63, 64]],
+[63, ["number", 270], 847, 577, [62, null]],
+[64, "forward", 789, 619, [62, 65, 66]],
+[65, ["number", 30], 859, 619, [64, null]],
+[66, "seth", 789, 661, [64, 67, 68]],
+[67, ["number", 18], 847, 661, [66, null]],
+[68, "pendown", 789, 703, [66, 69]],
+[69, ["repeat", 21], 789, 745, [68, 70, 71, 343]],
+[70, ["number", 5], 847, 745, [69, null]],
+[71, "forward", 807, 787, [69, 72, 73]],
+[72, ["number", 100], 877, 787, [71, null]],
+[73, "right", 807, 829, [71, 74, null]],
+[74, ["number", 144], 865, 829, [73, null]],
+[75, "seth", 789, 971, [343, 76, 344]],
+[76, ["number", 0], 847, 971, [75, null]],
+[77, "hat", 1043, 420, [null, 78, 367]],
+[78, ["string", "shapes"], 1101, 432, [77, null]],
+[79, "setpensize", 1026, 484, [367, 80, 353]],
+[80, ["number", 10], 1128, 484, [79, null]],
+[81, ["setxy2", 0], 1026, 568, [353, 82, 83, 354]],
+[82, ["number", -100], 1084, 568, [81, null]],
+[83, ["number", 100], 1084, 610, [81, null]],
+[84, "setcolor", 1026, 694, [354, 85, 86]],
+[85, "pop", 1103, 694, [84, null]],
+[86, "stack", 1026, 736, [84, 87, 355]],
+[87, "pop", 1084, 736, [86, null]],
+[88, ["setxy2", 0], 1026, 820, [355, 89, 90, 356]],
+[89, ["number", 100], 1084, 820, [88, null]],
+[90, ["number", 100], 1084, 862, [88, null]],
+[91, "setcolor", 1026, 946, [356, 92, 93]],
+[92, "pop", 1103, 946, [91, null]],
+[93, "stack", 1026, 988, [91, 94, 357]],
+[94, "pop", 1084, 988, [93, null]],
+[95, ["setxy2", 0], 1026, 1072, [357, 96, 97, 358]],
+[96, ["number", 100], 1084, 1072, [95, null]],
+[97, ["number", -100], 1084, 1114, [95, null]],
+[98, "setcolor", 1026, 1198, [358, 99, 100]],
+[99, "pop", 1103, 1198, [98, null]],
+[100, "stack", 1026, 1240, [98, 101, 359]],
+[101, "pop", 1084, 1240, [100, null]],
+[102, ["setxy2", 0], 1026, 1324, [359, 103, 104, 360]],
+[103, ["number", -100], 1084, 1324, [102, null]],
+[104, ["number", -100], 1084, 1366, [102, null]],
+[105, "setcolor", 1026, 1450, [360, 106, 107]],
+[106, "pop", 1103, 1450, [105, null]],
+[107, "stack", 1026, 1492, [105, 108, 361]],
+[108, "pop", 1084, 1492, [107, null]],
+[109, "hat", 1039, 263, [null, 110, 374]],
+[110, ["string", "calculate distance"], 1097, 275, [109, null]],
+[111, ["storein", 0], 1022, 327, [374, 112, 113, 352]],
+[112, ["string", "dx"], 1089, 327, [111, null]],
+[113, ["minus2", 0], 1089, 369, [111, 114, 116]],
+[114, "box", 1162, 369, [113, 115, null]],
+[115, ["string", "x"], 1216, 369, [114, null]],
+[116, "xcor", 1186, 411, [113, null]],
+[117, ["storein", 0], 1022, 453, [352, 118, 119, 351]],
+[118, ["string", "dy"], 1089, 453, [117, null]],
+[119, ["minus2", 0], 1089, 495, [117, 120, 122]],
+[120, "box", 1162, 495, [119, 121, null]],
+[121, ["string", "y"], 1216, 495, [120, null]],
+[122, "ycor", 1186, 537, [119, null]],
+[123, ["storein", 0], 1022, 579, [351, 124, 125, 332]],
+[124, ["string", "distance"], 1089, 579, [123, null]],
+[125, "sqrt", 1089, 621, [123, 126]],
+[126, ["plus2", 20.0], 1143, 621, [125, 127, 132]],
+[127, ["product2", 0], 1197, 621, [126, 128, 130]],
+[128, "box", 1251, 621, [127, 129, null]],
+[129, ["string", "dx"], 1305, 621, [128, null]],
+[130, "box", 1251, 663, [127, 131, null]],
+[131, ["string", "dx"], 1305, 663, [130, null]],
+[132, ["product2", 0], 1197, 703, [126, 133, 135]],
+[133, "box", 1251, 703, [132, 134, null]],
+[134, ["string", "dy"], 1305, 703, [133, null]],
+[135, "box", 1251, 745, [132, 136, null]],
+[136, ["string", "dy"], 1305, 745, [135, null]],
+[137, "hat", 516, 195, [null, 138, 379]],
+[138, ["string", "q1"], 574, 207, [137, null]],
+[139, ["storein", 0], 499, 259, [379, 140, 141, 142]],
+[140, ["string", "x"], 566, 259, [139, null]],
+[141, ["number", 100], 566, 301, [139, null]],
+[142, ["storein", 0], 499, 343, [139, 143, 144, 145]],
+[143, ["string", "y"], 566, 343, [142, null]],
+[144, ["number", 100], 566, 385, [142, null]],
+[145, "push", 499, 427, [142, 158, 146]],
+[146, "stack", 499, 469, [145, 147, 348]],
+[147, ["string", "ask"], 557, 469, [146, null]],
+[148, "hat", 518, 335, [null, 156, 380]],
+[149, ["storein", 0], 501, 399, [380, 150, 160, 151]],
+[150, ["string", "x"], 568, 399, [149, null]],
+[151, ["storein", 0], 501, 483, [149, 152, 159, 153]],
+[152, ["string", "y"], 568, 483, [151, null]],
+[153, "push", 501, 567, [151, 157, 154]],
+[154, "stack", 501, 609, [153, 155, 349]],
+[155, ["string", "ask"], 559, 609, [154, null]],
+[156, ["string", "q2"], 576, 347, [148, null]],
+[157, ["string", "Put the turtle in the red square."], 559, 567, [153, null]],
+[158, ["string", "Put the turtle in the blue triangle."], 557, 427, [145, null]],
+[159, ["number", 100], 568, 525, [151, null]],
+[160, ["number", -100], 568, 441, [149, null]],
+[161, "hat", 513, 477, [null, 162, 381]],
+[162, ["string", "q3"], 571, 489, [161, null]],
+[163, ["storein", 0], 496, 541, [381, 164, 183, 165]],
+[164, ["string", "x"], 563, 541, [163, null]],
+[165, ["storein", 0], 496, 625, [163, 166, 167, 168]],
+[166, ["string", "y"], 563, 625, [165, null]],
+[167, ["number", -100], 563, 667, [165, null]],
+[168, "push", 496, 709, [165, 182, 169]],
+[169, "stack", 496, 751, [168, 170, 350]],
+[170, ["string", "ask"], 554, 751, [169, null]],
+[171, "hat", 1052, 102, [null, 172, 382]],
+[172, ["string", "fini"], 1110, 114, [171, null]],
+[173, ["storein", 0], 1035, 166, [382, 176, 174, 175]],
+[174, ["number", 0], 1102, 208, [173, null]],
+[175, ["storein", 0], 1035, 250, [173, 177, 178, 179]],
+[176, ["string", "x"], 1102, 166, [173, null]],
+[177, ["string", "y"], 1102, 250, [175, null]],
+[178, ["number", 0], 1102, 292, [175, null]],
+[179, "push", 1035, 334, [175, 184, 180]],
+[180, "stack", 1035, 376, [179, 181, 342]],
+[181, ["string", "ask"], 1093, 376, [180, null]],
+[182, ["string", "Put the turtle in the red circle."], 554, 709, [168, null]],
+[183, ["number", -100], 563, 583, [163, null]],
+[184, ["string", "Well played!"], 1093, 334, [179, null]],
+[185, ["start", 2.0], 345, 201, [null, 371]],
+[186, ["storein", 0], 328, 257, [371, 187, 188, 189]],
+[187, ["string", "blue"], 395, 257, [186, null]],
+[188, ["number", 70], 395, 299, [186, null]],
+[189, ["storein", 0], 328, 341, [186, 190, 191, 192]],
+[190, ["string", "red"], 395, 341, [189, null]],
+[191, ["number", 0], 395, 383, [189, null]],
+[192, ["storein", 0], 328, 425, [189, 193, 194, 195]],
+[193, ["string", "green"], 395, 425, [192, null]],
+[194, ["number", 30], 395, 467, [192, null]],
+[195, "hideblocks", 328, 509, [192, 196]],
+[196, "stack", 328, 551, [195, 197, 198]],
+[197, ["string", "pattern a"], 386, 551, [196, null]],
+[198, "stack", 328, 593, [196, 199, 200]],
+[199, ["string", "q1"], 386, 593, [198, null]],
+[200, "stack", 328, 635, [198, 228, 201]],
+[201, "stack", 328, 677, [200, 202, 203]],
+[202, ["string", "q2"], 386, 677, [201, null]],
+[203, "stack", 328, 719, [201, 227, 204]],
+[204, "stack", 328, 761, [203, 205, 206]],
+[205, ["string", "q3"], 386, 761, [204, null]],
+[206, "stack", 328, 803, [204, 207, 340]],
+[207, ["string", "fini"], 386, 803, [206, null]],
+[208, "hat", 290, 493, [null, 209, 373]],
+[209, ["string", "answer"], 348, 505, [208, null]],
+[210, ["setxy2", 0], 273, 599, [385, 211, 212, 213]],
+[211, ["number", 0], 331, 599, [210, null]],
+[212, ["number", 0], 331, 641, [210, null]],
+[213, ["forever", 249], 273, 683, [210, 214, 334]],
+[214, "stack", 291, 717, [213, 215, 335]],
+[215, ["string", "calculate distance"], 349, 717, [214, null]],
+[216, ["if", 63], 291, 801, [335, 217, 221, 336]],
+[217, ["less2", 0], 347, 767, [216, 218, 220, null]],
+[218, "box", 403, 767, [217, 219, null]],
+[219, ["string", "distance"], 457, 767, [218, null]],
+[220, ["number", 100], 427, 809, [217, null]],
+[221, ["setxy2", 0], 309, 867, [216, 222, 223, 386]],
+[222, ["number", 0], 367, 867, [221, null]],
+[223, ["number", 0], 367, 909, [221, null]],
+[224, "stopstack", 309, 993, [386, null]],
+[225, "wait", 291, 1215, [336, 226, null]],
+[226, ["number", 1], 349, 1215, [225, null]],
+[227, ["string", "pattern c"], 386, 719, [203, null]],
+[228, ["string", "pattern b"], 386, 635, [200, null]],
+[229, "hat", 1313, 430, [null, 230, 370]],
+[230, ["string", "pattern a"], 1371, 442, [229, null]],
+[231, "clean", 1296, 494, [370, 232]],
+[232, "push", 1296, 536, [231, 233, 234]],
+[233, ["string", "square"], 1354, 536, [232, null]],
+[234, "push", 1296, 578, [232, 235, 237]],
+[235, "box", 1354, 578, [234, 236, null]],
+[236, ["string", "blue"], 1408, 578, [235, null]],
+[237, "push", 1296, 620, [234, 238, 239]],
+[238, ["string", "circle"], 1354, 620, [237, null]],
+[239, "push", 1296, 662, [237, 240, 242]],
+[240, "box", 1354, 662, [239, 241, null]],
+[241, ["string", "green"], 1408, 662, [240, null]],
+[242, "push", 1296, 704, [239, 243, 244]],
+[243, ["string", "triangle"], 1354, 704, [242, null]],
+[244, "push", 1296, 746, [242, 245, 247]],
+[245, "box", 1354, 746, [244, 246, null]],
+[246, ["string", "blue"], 1408, 746, [245, null]],
+[247, "push", 1296, 788, [244, 248, 249]],
+[248, ["string", "square"], 1354, 788, [247, null]],
+[249, "push", 1296, 830, [247, 250, 252]],
+[250, "box", 1354, 830, [249, 251, null]],
+[251, ["string", "red"], 1408, 830, [250, null]],
+[252, "stack", 1296, 872, [249, 253, 339]],
+[253, ["string", "shapes"], 1354, 872, [252, null]],
+[254, "hat", 1306, 268, [null, 255, 369]],
+[255, ["string", "pattern b"], 1364, 280, [254, null]],
+[256, "clean", 1289, 332, [369, 257]],
+[257, "push", 1289, 374, [256, 258, 259]],
+[258, ["string", "triangle"], 1347, 374, [257, null]],
+[259, "push", 1289, 416, [257, 260, 262]],
+[260, "box", 1347, 416, [259, 261, null]],
+[261, ["string", "red"], 1401, 416, [260, null]],
+[262, "push", 1289, 458, [259, 263, 264]],
+[263, ["string", "circle"], 1347, 458, [262, null]],
+[264, "push", 1289, 500, [262, 265, 267]],
+[265, "box", 1347, 500, [264, 266, null]],
+[266, ["string", "green"], 1401, 500, [265, null]],
+[267, "push", 1289, 542, [264, 268, 269]],
+[268, ["string", "square"], 1347, 542, [267, null]],
+[269, "push", 1289, 584, [267, 270, 272]],
+[270, "box", 1347, 584, [269, 271, null]],
+[271, ["string", "green"], 1401, 584, [270, null]],
+[272, "push", 1289, 626, [269, 273, 274]],
+[273, ["string", "square"], 1347, 626, [272, null]],
+[274, "push", 1289, 668, [272, 275, 277]],
+[275, "box", 1347, 668, [274, 276, null]],
+[276, ["string", "red"], 1401, 668, [275, null]],
+[277, "stack", 1289, 710, [274, 278, 338]],
+[278, ["string", "shapes"], 1347, 710, [277, null]],
+[279, "hat", 1296, 108, [null, 280, 368]],
+[280, ["string", "pattern c"], 1354, 120, [279, null]],
+[281, "clean", 1279, 172, [368, 282]],
+[282, "push", 1279, 214, [281, 283, 284]],
+[283, ["string", "circle"], 1337, 214, [282, null]],
+[284, "push", 1279, 256, [282, 285, 287]],
+[285, "box", 1337, 256, [284, 286, null]],
+[286, ["string", "red"], 1391, 256, [285, null]],
+[287, "push", 1279, 298, [284, 288, 289]],
+[288, ["string", "circle"], 1337, 298, [287, null]],
+[289, "push", 1279, 340, [287, 290, 292]],
+[290, "box", 1337, 340, [289, 291, null]],
+[291, ["string", "green"], 1391, 340, [290, null]],
+[292, "push", 1279, 382, [289, 293, 294]],
+[293, ["string", "square"], 1337, 382, [292, null]],
+[294, "push", 1279, 424, [292, 295, 297]],
+[295, "box", 1337, 424, [294, 296, null]],
+[296, ["string", "blue"], 1391, 424, [295, null]],
+[297, "push", 1279, 466, [294, 298, 299]],
+[298, ["string", "triangle"], 1337, 466, [297, null]],
+[299, "push", 1279, 508, [297, 300, 302]],
+[300, "box", 1337, 508, [299, 301, null]],
+[301, ["string", "red"], 1391, 508, [300, null]],
+[302, "stack", 1279, 550, [299, 303, 337]],
+[303, ["string", "shapes"], 1337, 550, [302, null]],
+[304, "hat", 39, 293, [null, 305, 372]],
+[305, ["string", "ask"], 97, 305, [304, null]],
+[306, ["setxy2", 0], 57, 423, [383, 307, 308, 384]],
+[307, ["number", -300], 115, 423, [306, null]],
+[308, ["number", 300], 115, 465, [306, null]],
+[309, "seth", 57, 549, [384, 310, 311]],
+[310, ["number", 90], 115, 549, [309, null]],
+[311, "setcolor", 57, 591, [309, 312, 313]],
+[312, ["number", 0], 134, 591, [311, null]],
+[313, "setshade", 57, 633, [311, 314, 315]],
+[314, ["number", 100], 142, 633, [313, null]],
+[315, "setpensize", 57, 675, [313, 316, 317]],
+[316, ["number", 120], 159, 675, [315, null]],
+[317, "forward", 57, 717, [315, 318, 319]],
+[318, ["number", 700], 127, 717, [317, null]],
+[319, "back", 57, 759, [317, 320, 321]],
+[320, ["number", 700], 115, 759, [319, null]],
+[321, "setshade", 57, 801, [319, 322, 323]],
+[322, ["number", 50], 142, 801, [321, null]],
+[323, "seth", 57, 843, [321, 324, 387]],
+[324, ["number", 0], 115, 843, [323, null]],
+[325, ["setxy2", 0], 57, 927, [387, 326, 327, 388]],
+[326, ["number", -300], 115, 927, [325, null]],
+[327, ["number", 300], 115, 969, [325, null]],
+[328, "show", 57, 1095, [389, 329, 330]],
+[329, "pop", 115, 1095, [328, null]],
+[330, "stack", 57, 1137, [328, 331, null]],
+[331, ["string", "answer"], 115, 1137, [330, null]],
+[332, ["vspace", 1], 1022, 663, [123, null]],
+[333, ["vspace", 1], 784, 597, [10, null]],
+[334, ["vspace", 1], 273, 1275, [213, null]],
+[335, ["vspace", 0], 291, 759, [214, 216]],
+[336, ["vspace", 60.0], 291, 1053, [216, 225]],
+[337, ["vspace", 1], 1279, 592, [302, null]],
+[338, ["vspace", 1], 1289, 752, [277, null]],
+[339, ["vspace", 1], 1296, 914, [252, null]],
+[340, "showblocks", 328, 845, [206, 341]],
+[341, ["vspace", 1], 328, 887, [340, null]],
+[342, ["vspace", 1], 1035, 418, [180, null]],
+[343, ["vspace", 20.0], 789, 889, [69, 75]],
+[344, ["vspace", 1], 789, 1013, [75, null]],
+[345, ["vspace", 1], 788, 1158, [53, null]],
+[346, ["vspace", 20.0], 788, 1034, [47, 53]],
+[347, ["vspace", 1], 779, 599, [27, null]],
+[348, ["vspace", 1], 499, 511, [146, null]],
+[349, ["vspace", 1], 501, 651, [154, null]],
+[350, ["vspace", 1], 496, 793, [169, null]],
+[351, ["vspace", 0], 1022, 537, [117, 123]],
+[352, ["vspace", 0], 1022, 411, [111, 117]],
+[353, "penup", 1026, 526, [79, 81]],
+[354, "pendown", 1026, 652, [81, 84]],
+[355, "penup", 1026, 778, [86, 88]],
+[356, "pendown", 1026, 904, [88, 91]],
+[357, "penup", 1026, 1030, [93, 95]],
+[358, "pendown", 1026, 1156, [95, 98]],
+[359, "penup", 1026, 1282, [100, 102]],
+[360, "pendown", 1026, 1408, [102, 105]],
+[361, "penup", 1026, 1534, [107, 363]],
+[362, "pendown", 1026, 1660, [363, 366]],
+[363, ["setxy2", 0], 1026, 1576, [361, 364, 365, 362]],
+[364, ["number", 0], 1084, 1576, [363, null]],
+[365, ["number", 0], 1084, 1618, [363, null]],
+[366, ["vspace", 1], 1026, 1702, [362, null]],
+[367, "sandwichclampcollapsed", 1043, 474, [77, 79, null]],
+[368, "sandwichclampcollapsed", 1296, 162, [279, 281, null]],
+[369, "sandwichclampcollapsed", 1306, 322, [254, 256, null]],
+[370, "sandwichclampcollapsed", 1313, 484, [229, 231, null]],
+[371, "sandwichclampcollapsed", 345, 247, [185, 186, null]],
+[372, "sandwichclampcollapsed", 39, 347, [304, 383, null]],
+[373, "sandwichclampcollapsed", 290, 547, [208, 385, null]],
+[374, "sandwichclampcollapsed", 1039, 317, [109, 111, null]],
+[375, "sandwichclampcollapsed", 796, 151, [13, 15, null]],
+[376, "sandwichclampcollapsed", 805, 586, [33, 35, null]],
+[377, "sandwichclampcollapsed", 801, 293, [0, 2, null]],
+[378, "sandwichclampcollapsed", 806, 441, [55, 57, null]],
+[379, "sandwichclampcollapsed", 516, 249, [137, 139, null]],
+[380, "sandwichclampcollapsed", 518, 389, [148, 149, null]],
+[381, "sandwichclampcollapsed", 513, 531, [161, 163, null]],
+[382, "sandwichclampcollapsed", 1052, 156, [171, 173, null]],
+[383, "penup", 57, 381, [372, 306]],
+[384, "pendown", 57, 507, [306, 309]],
+[385, "penup", 273, 557, [373, 210]],
+[386, "pendown", 309, 951, [221, 224]],
+[387, "penup", 57, 885, [323, 325]],
+[388, "pendown", 57, 1011, [325, 389]],
+[389, "setscale", 57, 1053, [388, 390, 328]],
+[390, ["number", 50.0], 134, 1053, [389, null]]]