From 86e2111461fab98d8c188d50ec8bd0c72c527f73 Mon Sep 17 00:00:00 2001 From: Pootle daemon Date: Mon, 13 Jun 2011 14:23:40 +0000 Subject: Merge branch 'master' of git.sugarlabs.org:turtleart/mainline --- (limited to 'TurtleArt/tabasics.py') diff --git a/TurtleArt/tabasics.py b/TurtleArt/tabasics.py new file mode 100644 index 0000000..bfed2d7 --- /dev/null +++ b/TurtleArt/tabasics.py @@ -0,0 +1,1270 @@ +# -*- coding: utf-8 -*- +#Copyright (c) 2011, Walter Bender + +#Permission is hereby granted, free of charge, to any person obtaining a copy +#of this software and associated documentation files (the "Software"), to deal +#in the Software without restriction, including without limitation the rights +#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +#copies of the Software, and to permit persons to whom the Software is +#furnished to do so, subject to the following conditions: + +#The above copyright notice and this permission notice shall be included in +#all copies or substantial portions of the Software. + +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +#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 +simply add a block of code to this file or to turtle_block_plugin.py, +which contains additional blocks. (Even better, write your own plugin!!) + + +Adding a new palette is simply a matter of: + palette = make_palette('mypalette', # the name of your palette + colors=["#00FF00", "#00A000"], + help_string=_('Palette of my custom commands')) + +For example, if we want to add a new turtle command, 'uturn', we'd use the +add_block method in the Palette class. + 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')) + + # 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)) + +That's it. When you next run Turtle Art, you will have a 'uturn' block +on the 'mypalette' palette. + +You will have to create icons for the palette-selector buttons. These +are kept in the icons subdirectory. You need two icons: +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 +from math import sqrt +from random import uniform + +from gettext import gettext as _ + +from tapalette import make_palette, define_logo_function +from talogo import primitive_dictionary, logoerror +from tautils import convert, chr_to_ord, round_int, strtype +from taconstants import BLACK, WHITE, CONSTANTS + +def _num_type(x): + """ Is x a number type? """ + if type(x) == int: + return True + if type(x) == float: + return True + if type(x) == ord: + return True + return False + + +def _millisecond(): + """ Current time in milliseconds """ + return time() * 1000 + + +class Palettes(): + """ a class for creating the palettes of blocks """ + + def __init__(self, parent): + self.tw = parent + + self._turtle_palette() + + self._pen_palette() + + self._color_palette() + + self._numbers_palette() + + self._flow_palette() + + self._blocks_palette() + + self._trash_palette() + + # Palette definitions + + def _turtle_palette(self): + """ The basic Turtle Art turtle palette """ + + palette = make_palette('turtle', + colors=["#00FF00", "#00A000"], + help_string=_('Palette of turtle commands')) + + primitive_dictionary['move'] = self._prim_move + palette.add_block('forward', + style='basic-style-1arg', + label=_('forward'), + prim_name='forward', + 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)) + + palette.add_block('back', + style='basic-style-1arg', + label=_('back'), + prim_name='back', + default=100, + logo_command='back', + help_string=_('moves turtle backward')) + self.tw.lc.def_prim('back', 1, + lambda self, x: primitive_dictionary['move']( + self.tw.canvas.forward, -x)) + + primitive_dictionary['clean'] = self.tw.lc.prim_clear + palette.add_block('clean', + style='basic-style-extended-vertical', + label=_('clean'), + prim_name='clean', + logo_command='clean', + help_string=_('clears the screen and reset the \ +turtle')) + self.tw.lc.def_prim('clean', 0, + lambda self: primitive_dictionary['clean']()) + + primitive_dictionary['right'] = self._prim_right + palette.add_block('left', + style='basic-style-1arg', + label=_('left'), + prim_name='left', + default=90, + logo_command='left', + help_string=_('turns turtle counterclockwise (angle \ +in degrees)')) + self.tw.lc.def_prim('left', 1, + lambda self, x: primitive_dictionary['right'](-x)) + + palette.add_block('right', + style='basic-style-1arg', + label=_('right'), + prim_name='right', + default=90, + 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)) + + primitive_dictionary['arc'] = self._prim_arc + palette.add_block('arc', + style='basic-style-2arg', + label=[_('arc'), _('angle'), _('radius')], + prim_name='arc', + 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)) + define_logo_function('taarc', 'to taarc :a :r\rrepeat round :a \ +[right 1 forward (0.0175 * :r)]\rend\r') + + palette.add_block('setxy2', + style='basic-style-2arg', + label=[_('set xy'), _('x'), _('y')], + prim_name='setxy2', + logo_command='tasetxy', + 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)) + define_logo_function('tasetxy', 'to tasetxy :x :y\rsetxy :x :y\rend\r') + + primitive_dictionary['set'] = self._prim_set + palette.add_block('seth', + style='basic-style-1arg', + label=_('set heading'), + prim_name='seth', + default=0, + 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)) + + palette.add_block('xcor', + style='box-style', + label=_('xcor'), + help_string=_('holds current x-coordinate value of \ +the turtle (can be used in place of a number block)'), + value_block=True, + prim_name='xcor', + logo_command='xcor') + self.tw.lc.def_prim( + 'xcor', 0, lambda self: self.tw.canvas.xcor / self.tw.coord_scale) + + palette.add_block('ycor', + style='box-style', + label=_('ycor'), + help_string=_('holds current y-coordinate value of \ +the turtle (can be used in place of a number block)'), + value_block=True, + prim_name='ycor', + logo_command='ycor') + self.tw.lc.def_prim( + 'ycor', 0, lambda self: self.tw.canvas.ycor / self.tw.coord_scale) + + palette.add_block('heading', + style='box-style', + label=_('heading'), + help_string=_('holds current heading value of the \ +turtle (can be used in place of a number block)'), + value_block=True, + prim_name='heading', + logo_command='heading') + self.tw.lc.def_prim( + 'heading', 0, lambda self: self.tw.canvas.heading) + + palette.add_block('turtle-label', + hidden=True, + style='blank-style', + label=['turtle']) + + # Deprecated + palette.add_block('setxy', + hidden=True, + style='basic-style-2arg', + label=[_('set xy'), _('x'), _('y')], + prim_name='setxy', + default=[0, 0], + 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)) + define_logo_function('tasetxypenup', 'to tasetxypenup :x :y\rpenup\r\ +setxy :x :y\rpendown\rend\r') + + def _pen_palette(self): + """ The basic Turtle Art pen palette """ + + palette = make_palette('pen', + colors=["#00FFFF", "#00A0A0"], + help_string=_('Palette of pen commands')) + + palette.add_block('penup', + style='basic-style-extended-vertical', + label=_('pen up'), + 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)) + + palette.add_block('pendown', + style='basic-style-extended-vertical', + label=_('pen down'), + 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)) + + palette.add_block('setpensize', + style='basic-style-1arg', + label=_('set pen size'), + prim_name='setpensize', + default=5, + 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\rsetpensize \ +round :a\rend\r') + + palette.add_block('fillscreen', + style='basic-style-2arg', + label=[_('fill screen'), _('color'), _('shade')], + prim_name='fillscreen', + default=[60, 80], + 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)) + define_logo_function('tasetbackground', 'to tasetbackground :color \ +:shade\rtasetshade :shade\rsetbackground :color\rend\r') + + palette.add_block('pensize', + style='box-style', + label=_('pen size'), + help_string=_('holds current pen size (can be used \ +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) + define_logo_function('tapensize', 'to tapensize\routput first round \ +pensize\rend\r') + + palette.add_block('startfill', + style='basic-style-extended-vertical', + label=_('start fill'), + 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()) + + palette.add_block('stopfill', + style='basic-style-extended-vertical', + label=_('end fill'), + 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()) + + def _color_palette(self): + """ The basic Turtle Art color palette """ + + palette = make_palette('colors', + colors=["#00FFFF", "#00A0A0"], + help_string=_('Palette of pen colors')) + + palette.add_block('setcolor', + style='basic-style-1arg', + label=_('set color'), + prim_name='setcolor', + default=0, + 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)) + + palette.add_block('setshade', + style='basic-style-1arg', + label=_('set shade'), + prim_name='setshade', + default=50, + 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)) + + palette.add_block('setgray', + style='basic-style-1arg', + label=_('set gray'), + prim_name='setgray', + 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)) + + palette.add_block('color', + style='box-style', + label=_('color'), + help_string=_('holds current pen color (can be used \ +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) + + palette.add_block('shade', + style='box-style', + label=_('shade'), + help_string=_('holds current pen shade'), + value_block=True, + prim_name='shade', + logo_command=':shade') + self.tw.lc.def_prim('shade', 0, lambda self: self.tw.canvas.shade) + + palette.add_block('gray', + style='box-style', + label=_('gray'), + help_string=_('holds current gray level (can be 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._make_constant(palette, 'red', CONSTANTS['red']) + self._make_constant(palette, 'orange', CONSTANTS['orange']) + self._make_constant(palette, 'yellow', CONSTANTS['yellow']) + self._make_constant(palette, 'green', CONSTANTS['green']) + self._make_constant(palette, 'cyan', CONSTANTS['cyan']) + self._make_constant(palette, 'blue', CONSTANTS['blue']) + self._make_constant(palette, 'purple', CONSTANTS['purple']) + self._make_constant(palette, 'white', WHITE) + self._make_constant(palette, 'black', 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', '\ +to tasetpalette :i :r :g :b :myshade \r\ +make "s ((:myshade - 50) / 50) \r\ +ifelse lessp :s 0 [ \r\ +make "s (1 + (:s *0.8)) \r\ +make "r (:r * :s) \r\ +make "g (:g * :s) \r\ +make "b (:b * :s) \r\ +] [ \ +make "s (:s * 0.9) \r\ +make "r (:r + ((99-:r) * :s)) \r\ +make "g (:g + ((99-:g) * :s)) \r\ +make "b (:b + ((99-:b) * :s)) \r\ +] \ +setpalette :i (list :r :g :b) \r\ +end \r\ +\ +to rgb :myi :mycolors :myshade \r\ +make "myr first :mycolors \r\ +make "mycolors butfirst :mycolors \r\ +make "myg first :mycolors \r\ +make "mycolors butfirst :mycolors \r\ +make "myb first :mycolors \r\ +make "mycolors butfirst :mycolors \r\ +tasetpalette :myi :myr :myg :myb :myshade \r\ +output :mycolors \r\ +end \r\ +\ +to processcolor :mycolors :myshade \r\ +if emptyp :mycolors [stop] \r\ +make "i :i + 1 \r\ +processcolor (rgb :i :mycolors :myshade) :myshade \r\ +end \r\ +\ +to tasetshade :shade \r\ +make "myshade modulo :shade 200 \r\ +if greaterp :myshade 99 [make "myshade (199-:myshade)] \r\ +make "i 7 \r\ +make "mycolors :colors \r\ +processcolor :mycolors :myshade \r\ +end \r\ +\ +to tasetpencolor :c \r\ +make "color (modulo (round :c) 100) \r\ +setpencolor :color + 8 \r\ +end \r\ +\ +make "colors [ \ +99 0 0 99 5 0 99 10 0 99 15 0 99 20 0 \ +99 25 0 99 30 0 99 35 0 99 40 0 99 45 0 \ +99 50 0 99 55 0 99 60 0 99 65 0 99 70 0 \ +99 75 0 99 80 0 99 85 0 99 90 0 99 95 0 \ +99 99 0 90 99 0 80 99 0 70 99 0 60 99 0 \ +50 99 0 40 99 0 30 99 0 20 99 0 10 99 0 \ + 0 99 0 0 99 5 0 99 10 0 99 15 0 99 20 \ + 0 99 25 0 99 30 0 99 35 0 99 40 0 99 45 \ + 0 99 50 0 99 55 0 99 60 0 99 65 0 99 70 \ + 0 99 75 0 99 80 0 99 85 0 99 90 0 99 95 \ + 0 99 99 0 95 99 0 90 99 0 85 99 0 80 99 \ + 0 75 99 0 70 99 0 65 99 0 60 99 0 55 99 \ + 0 50 99 0 45 99 0 40 99 0 35 99 0 30 99 \ + 0 25 99 0 20 99 0 15 99 0 10 99 0 5 99 \ + 0 0 99 5 0 99 10 0 99 15 0 99 20 0 99 \ +25 0 99 30 0 99 35 0 99 40 0 99 45 0 99 \ +50 0 99 55 0 99 60 0 99 65 0 99 70 0 99 \ +75 0 99 80 0 99 85 0 99 90 0 99 95 0 99 \ +99 0 99 99 0 90 99 0 80 99 0 70 99 0 60 \ +99 0 50 99 0 40 99 0 30 99 0 20 99 0 10] \r\ +make "shade 50 \r\ +tasetshade :shade \r') + + def _numbers_palette(self): + """ The basic Turtle Art numbers palette """ + + palette = make_palette('numbers', + colors=["#FF00FF", "#A000A0"], + help_string=_('Palette of numeric operators')) + + primitive_dictionary['plus'] = self._prim_plus + palette.add_block('plus2', + style='number-style', + label='+', + special_name=_('plus'), + prim_name='plus', + logo_command='sum', + help_string=_('adds two alphanumeric inputs')) + self.tw.lc.def_prim( + 'plus', 2, lambda self, x, y: primitive_dictionary['plus'](x, y)) + + primitive_dictionary['minus'] = self._prim_minus + palette.add_block('minus2', + style='number-style-porch', + label='–', + special_name=_('minus'), + prim_name='minus', + logo_command='taminus', + help_string=_('subtracts bottom numeric input from \ +top numeric input')) + self.tw.lc.def_prim( + 'minus', 2, lambda self, x, y: primitive_dictionary['minus'](x, y)) + define_logo_function('taminus', 'to taminus :y :x\routput sum :x minus \ +:y\rend\r') + + primitive_dictionary['product'] = self._prim_product + palette.add_block('product2', + style='number-style', + label='×', + special_name=_('multiply'), + prim_name='product', + logo_command='product', + help_string=_('multiplies two numeric inputs')) + self.tw.lc.def_prim( + 'product', 2, + lambda self, x, y: primitive_dictionary['product'](x, y)) + + primitive_dictionary['division'] = self._prim_careful_divide + palette.add_block('division2', + style='number-style-porch', + label='/', + special_name=_('divide'), + prim_name='division', + logo_command='quotient', + help_string=_('divides top numeric input (numerator) \ +by bottom numeric input (denominator)')) + self.tw.lc.def_prim( + 'division', 2, + lambda self, x, y: primitive_dictionary['division'](x, y)) + + primitive_dictionary['id'] = self._prim_identity + palette.add_block('identity2', + style='number-style-1arg', + label='←', + special_name=_('identity'), + prim_name='id', + help_string=_('identity operator used for extending \ +blocks')) + self.tw.lc.def_prim('id', 1, + lambda self, x: primitive_dictionary['id'](x)) + + primitive_dictionary['remainder'] = self._prim_mod + palette.add_block('remainder2', + style='number-style-porch', + label=_('mod'), + special_name=_('mod'), + prim_name='remainder', + logo_command='remainder', + help_string=_('modular (remainder) operator')) + self.tw.lc.def_prim('remainder', 2, + lambda self, x, y: primitive_dictionary['remainder'](x, y)) + + primitive_dictionary['sqrt'] = self._prim_sqrt + palette.add_block('sqrt', + style='number-style-1arg', + label=_('√'), + special_name=_('square root'), + prim_name='sqrt', + logo_command='tasqrt', + help_string=_('calculates square root')) + self.tw.lc.def_prim('sqrt', 1, + lambda self, x: primitive_dictionary['sqrt'](x)) + + primitive_dictionary['random'] = self._prim_random + palette.add_block('random', + style='number-style-block', + label=[_('random'), _('min'), _('max')], + default=[0, 100], + prim_name='random', + logo_command='tarandom', + help_string=_('returns random number between minimum \ +(top) and maximum (bottom) values')) + self.tw.lc.def_prim( + 'random', 2, lambda self, x, y: primitive_dictionary['random']( + x, y)) + define_logo_function('tarandom', 'to tarandom :min :max\r \ +output (random (:max - :min)) + :min\rend\r') + + palette.add_block('number', + style='box-style', + label='100', + default=100, + special_name=_('number'), + help_string=_('used as numeric input in mathematic \ +operators')) + + primitive_dictionary['more'] = self._prim_more + palette.add_block('greater2', + style='compare-porch-style', + label='>', + special_name=_('greater than'), + prim_name='greater?', + logo_command='greater?', + help_string=_('logical greater-than operator')) + self.tw.lc.def_prim( + 'greater?', 2, lambda self, x, y: primitive_dictionary['more'](x, y)) + + primitive_dictionary['less'] = self._prim_less + palette.add_block('less2', + style='compare-porch-style', + label='<', + special_name=_('less than'), + prim_name='less?', + logo_command='less?', + help_string=_('logical less-than operator')) + self.tw.lc.def_prim( + 'less?', 2, lambda self, x, y: primitive_dictionary['less'](x, y)) + + primitive_dictionary['equal'] = self._prim_equal + palette.add_block('equal2', + style='compare-style', + label='=', + special_name=_('equal'), + prim_name='equal?', + logo_command='equal?', + help_string=_('logical equal-to operator')) + self.tw.lc.def_prim( + 'equal?', 2, lambda self, x, y: primitive_dictionary['equal'](x, y)) + + palette.add_block('not', + style='not-style', + label=_('not'), + prim_name='not', + logo_command='not', + help_string=_('logical NOT operator')) + self.tw.lc.def_prim('not', 1, lambda self, x: not x) + + primitive_dictionary['and'] = self._prim_and + palette.add_block('and2', + style='boolean-style', + label=_('and'), + prim_name='and', + logo_command='and', + special_name=_('and'), + help_string=_('logical AND operator')) + self.tw.lc.def_prim( + 'and', 2, lambda self, x, y: primitive_dictionary['and'](x, y)) + + primitive_dictionary['or'] = self._prim_or + palette.add_block('or2', + style='boolean-style', + label=_('or'), + prim_name='or', + logo_command='or', + special_name=_('or'), + help_string=_('logical OR operator')) + self.tw.lc.def_prim( + 'or', 2, lambda self, x, y: primitive_dictionary['or'](x, y)) + + def _flow_palette(self): + """ The basic Turtle Art flow palette """ + + palette = make_palette('flow', + colors=["#FFC000", "#A08000"], + help_string=_('Palette of flow operators')) + + primitive_dictionary['wait'] = self._prim_wait + palette.add_block('wait', + style='basic-style-1arg', + label=_('wait'), + prim_name='wait', + default=1, + logo_command='wait', + help_string=_('pauses program execution a specified \ +number of seconds')) + self.tw.lc.def_prim('wait', 1, primitive_dictionary['wait'], True) + + primitive_dictionary['forever'] = self._prim_forever + palette.add_block('forever', + style='flow-style', + label=_('forever'), + prim_name='forever', + default=[None, 'vspace'], + logo_command='forever', + help_string=_('loops forever')) + self.tw.lc.def_prim('forever', 1, primitive_dictionary['forever'], True) + + primitive_dictionary['repeat'] = self._prim_repeat + palette.add_block('repeat', + style='flow-style-1arg', + label=[' ', _('repeat')], + prim_name='repeat', + default=[4, None, 'vspace'], + logo_command='repeat', + special_name=_('repeat'), + help_string=_('loops specified number of times')) + self.tw.lc.def_prim('repeat', 2, primitive_dictionary['repeat'], True) + + primitive_dictionary['if'] = self._prim_if + palette.add_block('if', + style='flow-style-boolean', + label=[' ', _('if'), _('then')], + prim_name='if', + default=[None, None, 'vspace'], + special_name=_('if then'), + logo_command='if', + help_string=_('if-then operator that uses boolean \ +operators from Numbers palette')) + self.tw.lc.def_prim('if', 2, primitive_dictionary['if'], True) + + primitive_dictionary['ifelse'] = self._prim_ifelse + palette.add_block('ifelse', + style='flow-style-else', + label=[' ', _('if'), _('then else')], + prim_name='ifelse', + default=[None, 'vspace', None, 'vspace'], + logo_command='ifelse', + special_name=_('if then else'), + help_string=_('if-then-else operator that uses \ +boolean operators from Numbers palette')) + self.tw.lc.def_prim('ifelse', 3, primitive_dictionary['ifelse'], True) + + palette.add_block('hspace', + style='flow-style-tail', + label=' ', + prim_name='nop', + special_name=_('horizontal space'), + help_string=_('jogs stack right')) + self.tw.lc.def_prim('nop', 0, lambda self: None) + + palette.add_block('vspace', + style='basic-style-extended-vertical', + label=' ', + prim_name='nop', + special_name=_('vertical space'), + help_string=_('jogs stack down')) + self.tw.lc.def_prim('nop', 0, lambda self: None) + + primitive_dictionary['stopstack'] = self._prim_stopstack + palette.add_block('stopstack', + style='basic-style-tail', + label=_('stop action'), + prim_name='stopstack', + logo_command='stop', + help_string=_('stops current action')) + self.tw.lc.def_prim('stopstack', 0, + lambda self: primitive_dictionary['stopstack']()) + + def _blocks_palette(self): + """ The basic Turtle Art blocks palette """ + + palette = make_palette('blocks', + colors=["#FFFF00", "#A0A000"], + help_string=_('Palette of variable blocks')) + + primitive_dictionary['start'] = self._prim_start + palette.add_block('start', + style='basic-style-head', + label=_('start'), + prim_name='start', + logo_command='to start\r', + help_string=_('connects action to toolbar run \ +buttons')) + self.tw.lc.def_prim('start', 0, + lambda self: primitive_dictionary['start']()) + + primitive_dictionary['setbox'] = self._prim_setbox + palette.add_block('storeinbox1', + style='basic-style-1arg', + label=_('store in box 1'), + prim_name='storeinbox1', + default=100, + logo_command='make "box1', + help_string=_('stores numeric value in Variable 1')) + self.tw.lc.def_prim('storeinbox1', 1, + lambda self, x: primitive_dictionary['setbox']( + 'box1', None, x)) + + palette.add_block('storeinbox2', + style='basic-style-1arg', + label=_('store in box 2'), + prim_name='storeinbox2', + default=100, + logo_command='make "box2', + help_string=_('stores numeric value in Variable 2')) + self.tw.lc.def_prim('storeinbox2', 1, + lambda self, x: primitive_dictionary['setbox']( + 'box2', None, x)) + + palette.add_block('string', + style='box-style', + label=_('text'), + default=_('text'), + special_name=_('text'), + help_string=_('string value')) + + palette.add_block('box1', + style='box-style', + label=_('box 1'), + prim_name='box1', + logo_command=':box1', + help_string=_('Variable 1 (numeric value)'), + value_block=True) + self.tw.lc.def_prim('box1', 0, lambda self: self.tw.lc.boxes['box1']) + + palette.add_block('box2', + style='box-style', + label=_('box 2'), + prim_name='box2', + logo_command=':box2', + help_string=_('Variable 2 (numeric value)'), + value_block=True) + self.tw.lc.def_prim('box2', 0, lambda self: self.tw.lc.boxes['box2']) + + primitive_dictionary['box'] = self._prim_box + palette.add_block('box', + style='number-style-1strarg', + label=_('box'), + prim_name='box', + default=_('my box'), + logo_command='box', + help_string=_('named variable (numeric value)')) + self.tw.lc.def_prim('box', 1, + lambda self, x: primitive_dictionary['box'](x)) + + palette.add_block('storein', + style='basic-style-2arg', + label=[_('store in'), _('box'), _('value')], + prim_name='storeinbox', + logo_command='storeinbox', + default=[_('my box'), 100], + help_string=_('stores numeric value in named \ +variable')) + self.tw.lc.def_prim('storeinbox', 2, + lambda self, x, y: primitive_dictionary['setbox']( + 'box3', x, y)) + + palette.add_block('hat', + style='basic-style-head-1arg', + label=_('action'), + prim_name='nop3', + default=_('stack'), + logo_command='to action', + help_string=_('top of nameable action stack')) + self.tw.lc.def_prim('nop3', 1, lambda self, x: None) + + palette.add_block('hat1', + style='basic-style-head', + label=_('action 1'), + prim_name='nop1', + logo_command='to stack1\r', + help_string=_('top of Action 1 stack')) + self.tw.lc.def_prim('nop1', 0, lambda self: None) + + palette.add_block('hat2', + style='basic-style-head', + label=_('action 2'), + prim_name='nop2', + logo_command='to stack2\r', + help_string=_('top of Action 2 stack')) + self.tw.lc.def_prim('nop2', 0, lambda self: None) + + primitive_dictionary['stack'] = self._prim_stack + palette.add_block('stack', + style='basic-style-1arg', + label=_('action'), + prim_name='stack', + logo_command='action', + default=_('action'), + help_string=_('invokes named action stack')) + self.tw.lc.def_prim('stack', 1, primitive_dictionary['stack'], True) + + primitive_dictionary['stack1'] = self._prim_stack1 + palette.add_block('stack1', + style='basic-style-extended-vertical', + label=_('action 1'), + prim_name='stack1', + logo_command='stack1', + help_string=_('invokes Action 1 stack')) + self.tw.lc.def_prim('stack1', 0, primitive_dictionary['stack1'], True) + + primitive_dictionary['stack2'] = self._prim_stack2 + palette.add_block('stack2', + style='basic-style-extended-vertical', + label=_('action 2'), + prim_name='stack2', + logo_command='stack2', + help_string=_('invokes Action 2 stack')) + self.tw.lc.def_prim('stack2', 0, primitive_dictionary['stack2'], True) + + def _trash_palette(self): + """ The basic Turtle Art turtle palette """ + + palette = make_palette('trash', + colors=["#FFFF00", "#A0A000"], + help_string=_('trash')) + + palette.add_block('empty', + style='basic-style-tail', + label=_('empty trash'), + help_string=_('permanently deletes items in trash')) + + palette.add_block('restoreall', + style='basic-style-head', + label=_('restore all'), + help_string=_('restore all blocks from trash')) + + palette.add_block('trashall', + style='basic-style-tail', + label=_('clear all'), + help_string=_('move all blocks to trash')) + + # Block primitives + + def _prim_and(self, x, y): + """ Logical and """ + return x & y + + def _prim_arc(self, cmd, value1, value2): + """ Turtle draws an arc of degree, radius """ + cmd(float(value1), float(value2)) + self.tw.lc.update_label_value( + 'xcor', self.tw.canvas.xcor / 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) + + def _prim_box(self, x): + """ Retrieve value from named box """ + if type(convert(x, float, False)) == float: + if int(float(x)) == x: + x = int(x) + try: + return self.tw.lc.boxes['box3' + str(x)] + except KeyError: + raise logoerror("#emptybox") + + def _prim_forever(self, blklist): + """ Do list forever """ + while True: + self.tw.lc.icall(self.tw.lc.evline, blklist[:]) + yield True + if self.tw.lc.procstop: + break + self.tw.lc.ireturn() + yield True + + def _prim_if(self, boolean, blklist): + """ If bool, do list """ + if boolean: + self.tw.lc.icall(self.tw.lc.evline, blklist[:]) + yield True + self.tw.lc.ireturn() + yield True + + def _prim_ifelse(self, boolean, list1, list2): + """ If bool, do list1, else do list2 """ + if boolean: + self.tw.lc.ijmp(self.tw.lc.evline, list1[:]) + yield True + else: + self.tw.lc.ijmp(self.tw.lc.evline, list2[:]) + yield True + + def _prim_move(self, cmd, value1, value2=None, pendown=True): + """ Turtle moves by method specified in value1 """ + if value2 is None: + cmd(value1) + else: + cmd(float(value1), float(value2), pendown=pendown) + self.tw.lc.update_label_value('xcor', + self.tw.canvas.xcor / self.tw.coord_scale) + self.tw.lc.update_label_value('ycor', + self.tw.canvas.ycor / self.tw.coord_scale) + + def _prim_or(self, x, y): + """ Logical or """ + return x | y + + def _prim_repeat(self, num, blklist): + """ Repeat list num times. """ + num = self.tw.lc.int(num) + for i in range(num): + self.tw.lc.icall(self.tw.lc.evline, blklist[:]) + yield True + if self.tw.lc.procstop: + break + self.tw.lc.ireturn() + yield True + + def _prim_right(self, value): + """ Turtle rotates clockwise """ + self.tw.canvas.right(float(value)) + self.tw.lc.update_label_value('heading', self.tw.canvas.heading) + + def _prim_set(self, name, cmd, value=None): + """ Set a value and update the associated value blocks """ + if value is not None: + cmd(value) + self.tw.lc.update_label_value(name, value) + + def _prim_setbox(self, name, x, val): + """ Define value of named box """ + if x is not None: + if type(convert(x, float, False)) == float: + if int(float(x)) == x: + x = int(x) + self.tw.lc.boxes[name + str(x)] = val + return + + self.tw.lc.boxes[name] = val + self.tw.lc.update_label_value(name, val) + + def _prim_stack(self, x): + """ Process a named stack """ + if type(convert(x, float, False)) == float: + if int(float(x)) == x: + x = int(x) + if 'stack3' + str(x) not in self.tw.lc.stacks or \ + self.tw.lc.stacks['stack3' + str(x)] is None: + raise logoerror("#nostack") + self.tw.lc.icall(self.tw.lc.evline, + self.tw.lc.stacks['stack3' + str(x)][:]) + yield True + self.tw.lc.procstop = False + self.tw.lc.ireturn() + yield True + + def _prim_stack1(self): + """ Process Stack 1 """ + if self.tw.lc.stacks['stack1'] is None: + raise logoerror("#nostack") + self.tw.lc.icall(self.tw.lc.evline, + self.tw.lc.stacks['stack1'][:]) + yield True + self.tw.lc.procstop = False + self.tw.lc.ireturn() + yield True + + def _prim_stack2(self): + """ 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'][:]) + yield True + self.tw.lc.procstop = False + self.tw.lc.ireturn() + yield True + + def _prim_start(self): + """ Start block: recenter """ + if self.tw.running_sugar: + self.tw.activity.recenter() + + def _prim_stopstack(self): + """ 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() + endtime = _millisecond() + wait_time * 1000. + while _millisecond() < endtime: + yield True + self.tw.active_turtle.hide() + self.tw.lc.ireturn() + yield True + + # Math primitivies + + def _prim_careful_divide(self, x, y): + """ Raise error on divide by zero """ + try: + return x / y + except ZeroDivisionError: + raise logoerror("#zerodivide") + except TypeError: + try: + return self._string_to_num(x) / self._string_to_num(y) + except ZeroDivisionError: + raise logoerror("#zerodivide") + except ValueError: + raise logoerror("#syntaxerror") + except TypeError: + raise logoerror("#notanumber") + + def _prim_equal(self, x, y): + """ Numeric and logical equal """ + try: + return float(x) == float(y) + except TypeError: + typex, typey = False, False + if strtype(x): + typex = True + if strtype(y): + typey = True + if typex and typey: + return x == y + try: + return self._string_to_num(x) == self._string_to_num(y) + except ValueError: + raise logoerror("#syntaxerror") + + def _prim_less(self, x, y): + """ Compare numbers and strings """ + try: + return float(x) < float(y) + except ValueError: + typex, typey = False, False + if strtype(x): + typex = True + if strtype(y): + typey = True + if typex and typey: + return x < y + try: + return self._string_to_num(x) < self._string_to_num(y) + except TypeError: + raise logoerror("#notanumber") + + def _prim_more(self, x, y): + """ Compare numbers and strings """ + return self._prim_less(y, x) + + def _prim_plus(self, x, y): + """ Add numbers, concat strings """ + if _num_type(x) and _num_type(y): + return(x + y) + else: + if _num_type(x): + xx = str(round_int(x)) + else: + xx = str(x) + if _num_type(y): + yy = str(round_int(y)) + else: + yy = str(y) + return(xx + yy) + + def _prim_minus(self, x, y): + """ Numerical subtraction """ + if _num_type(x) and _num_type(y): + return(x - y) + try: + return self._string_to_num(x) - self._string_to_num(y) + except TypeError: + raise logoerror("#notanumber") + + def _prim_product(self, x, y): + """ Numerical multiplication """ + if _num_type(x) and _num_type(y): + return(x * y) + try: + return self._string_to_num(x) * self._string_to_num(y) + except TypeError: + raise logoerror("#notanumber") + + def _prim_mod(self, x, y): + """ Numerical mod """ + if _num_type(x) and _num_type(y): + return(x % y) + try: + return self._string_to_num(x) % self._string_to_num(y) + except TypeError: + raise logoerror("#notanumber") + except ValueError: + raise logoerror("#syntaxerror") + + def _prim_sqrt(self, x): + """ Square root """ + if _num_type(x): + if x < 0: + raise logoerror("#negroot") + return sqrt(x) + try: + return sqrt(self._string_to_num(x)) + except ValueError: + raise logoerror("#negroot") + except TypeError: + raise logoerror("#notanumber") + + def _prim_random(self, x, y): + """ Random integer """ + if _num_type(x) and _num_type(y): + return(int(round(uniform(x, y), 0))) + xx, xflag = chr_to_ord(x) + yy, yflag = chr_to_ord(y) + if xflag and yflag: + return chr(int(round(uniform(xx, yy), 0))) + if not xflag: + xx = self._string_to_num(x) + if not yflag: + yy = self._string_to_num(y) + try: + return(int(round(uniform(xx, yy), 0))) + except TypeError: + raise logoerror("#notanumber") + + def _prim_identity(self, x): + """ Identity function """ + return(x) + + # Utilities + + def _string_to_num(self, x): + """ Try to comvert a string to a number """ + if type(x) is float: + return(x) + if type(x) is int: + return(x) + if type(x) is ord: + return(int(x)) + xx = convert(x.replace(self.tw.decimal_point, '.'), float) + if type(xx) is float: + return xx + else: + xx, xflag = chr_to_ord(x) + if xflag: + return xx + else: + raise logoerror("#syntaxerror") + + def _make_constant(self, palette, block_name, constant): + """ Factory for constant blocks """ + palette.add_block(block_name, style='box-style', + label=_(block_name), prim_name=block_name, + logo_command=block_name) + self.tw.lc.def_prim(block_name, 0, lambda self: constant) -- cgit v0.9.1