Files
pyscript/pyscript.core/test/multi-turtle/turtle.py
2023-07-12 17:55:39 +02:00

2487 lines
71 KiB
Python

"""
A port of Basthon's Turtle module for PyScript (it works with both Pyodide
and MicroPython interpreters), by Nicholas H.Tollervey under the GPLv3
License. ntollervey@anaconda.com
A port of the Brython's Turtle module to Basthon by Romain Casati
under the GPLv3 License.
A revised version of CPython's turtle module written for Brython
Note: This version is not intended to be used in interactive mode,
nor use help() to look up methods/functions definitions. The docstrings
have thus been shortened considerably as compared with the CPython's version.
All public methods/functions of the CPython version should exist, if only
to print out a warning that they are not implemented. The intent is to make
it easier to "port" any existing turtle program from CPython to the browser.
IMPORTANT: We use SVG for drawing turtles. If we have a turtle at an angle
of 350 degrees and we rotate it by an additional 20 degrees, we will have
a turtle at an angle of 370 degrees. For turtles drawn periodically on
a screen (like typical animations, including the CPython turtle module),
drawing a turtle with a rotation of 370 degrees is the same as a rotation of
10 degrees. However, using SVG, if we "slowly" animate an object,
rotating it from 350 to 370 degrees, the result will not be the same
as rotating it from 350 to 10 degree.
"""
import math
import sys
import random
from math import cos, sin
import svg as SVG
def appendTo(root, node):
root.appendChild(node)
def generate_id():
chars = "abcdefghijklmnopqrstuvwxyz1234567890"
return "".join([random.choice(chars) for i in range(16)])
# Even though it is a private object, use the same name for the configuration
# dict as the CPython's module.
# Commented out configuration items are those found on the CPython version
def _default_cfg():
return {
# "width" : 0.5, # Screen
# "height" : 0.75,
"canvwidth": 640,
"canvheight": 480,
# "leftright": None,
# "topbottom": None,
"mode": "standard",
# "colormode": 1.0,
# "delay": 10,
# "undobuffersize": 1000,
"shape": "turtle",
"pencolor": "black",
"fillcolor": "black",
# "resizemode" : "noresize",
"visible": True,
# "language": "english", # docstrings
# "exampleturtle": "turtle",
# "examplescreen": "screen",
# "title": "Python Turtle Graphics",
# "using_IDLE": False
# Below are configuration items specific to this version
"min_duration": "1ms",
}
_CFG = _default_cfg()
def set_defaults(**params):
"""Allows to override defaults."""
_CFG.update(**params)
Screen().reset()
class Vec2D(tuple):
"""
Used to give a nicer representation of the position.
Because we're using SVG, if we "slowly" animate an object, rotating it
from 350 to 370 degrees, the result will not be the same as rotating it
from 350 to 10 degrees. For this reason, we did not use the Vec2D class
from the CPython module and handle the rotations quite differently.
This version of Vec2D is implemented for completeness.
Provides (for a, b vectors, k number):
a+b vector addition
a-b vector subtraction
a*b inner product
k*a and a*k multiplication with scalar
|a| absolute value of a
As mentioned above, does not provide a.rotate(ange) rotation because of
SVG reasons.
"""
def __add__(self, other):
return Vec2D(self[0] + other[0], self[1] + other[1])
def __mul__(self, other):
if isinstance(other, Vec2D):
return self[0] * other[0] + self[1] * other[1]
return Vec2D(self[0] * other, self[1] * other)
def __rmul__(self, other):
if isinstance(other, int) or isinstance(other, float):
return Vec2D(self[0] * other, self[1] * other)
return NotImplemented
def __sub__(self, other):
return Vec2D(self[0] - other[0], self[1] - other[1])
def __neg__(self):
return Vec2D(-self[0], -self[1])
def __abs__(self):
return math.sqrt(sum(i**2 for i in self))
def __getnewargs__(self):
return (self[0], self[1])
def __repr__(self):
return "(%.2f, %.2f)" % self
def create_circle(r):
"""Creates a circle of radius r centered at the origin"""
circle = SVG.circle(x=0, y=0, r=r, stroke="black", fill="black")
circle.setAttribute("stroke-width", 1)
return circle
def create_polygon(points):
"""Creates a polygon using the points provided"""
points = " ".join(",".join(map(str, p)) for p in points)
polygon = SVG.polygon(points=points, stroke="black", fill="black")
polygon.setAttribute("stroke-width", 1)
return polygon
def create_rectangle(width=2, height=2, rx=None, ry=None):
"""Creates a rectangle centered at the origin. rx and ry can be
used to have rounded corners"""
rectangle = SVG.rect(
x=-width / 2,
y=-height / 2,
width=width,
height=height,
stroke="black",
fill="black",
)
rectangle.setAttribute("stroke-width", 1)
if rx is not None:
rectangle.setAttribute("rx", rx)
if ry is not None:
rectangle.setAttribute("ry", ry)
return rectangle
def create_square(size=2, r=None):
"""Creates a square centered at the origin. rx and ry can be
used to have rounded corners"""
return create_rectangle(width=size, height=size, rx=r, ry=r)
class TurtleGraphicsError(Exception):
"""Some TurtleGraphics Error"""
pass
class Screen:
_instance = None
_initialised = False
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = object.__new__(cls, *args, **kwargs)
return cls._instance
def __init__(self):
if not self._initialised:
self.shapes = {
"arrow": (create_polygon, ((-10, 0), (10, 0), (0, 10))),
"turtle": (
create_polygon,
(
(0, 16),
(-2, 14),
(-1, 10),
(-4, 7),
(-7, 9),
(-9, 8),
(-6, 5),
(-7, 1),
(-5, -3),
(-8, -6),
(-6, -8),
(-4, -5),
(0, -7),
(4, -5),
(6, -8),
(8, -6),
(5, -3),
(7, 1),
(6, 5),
(9, 8),
(7, 9),
(4, 7),
(1, 10),
(2, 14),
),
),
"classic": (
create_polygon,
((0, 0), (-5, -9), (0, -7), (5, -9)),
),
"triangle": (
create_polygon,
((10, -5.77), (0, 11.55), (-10, -5.77)),
),
"square": (create_square, 20),
"circle": (create_circle, 10),
}
self._animate = True
self._old_svg_scene = None
self.reset()
self._initialised = True
def animation(self, onoff):
onoff = onoff.lower()
if onoff == "on":
self._animate = True
elif onoff == "off":
self._animate = False
else:
raise ValueError("Supported values are only 'on' and 'off'.")
def _repr_svg_(self):
return self._old_svg_scene.outerHTML
def svg(self):
return self._repr_svg_()
def save(self, file):
"""Save SVG to a file.
file can be a file descriptor or a filename.
If file is a file descriptor, it should be open in text mode.
"""
if self._old_svg_scene is None:
raise RuntimeError(
"No turtle scene ended! " "You should call 'done' first."
)
html = self._old_svg_scene.outerHTML
if isinstance(file, str):
with open(file, "w") as f:
f.write(html)
else:
# file should be a file descriptor
file.write(html)
def animation_frame_id(self, index):
return "af_{}_{}".format(self.svg_id, index)
def bgcolor(self, color=None):
"""sets the background with the given color if color is not None,
else return current background color.
"""
if color is None:
return self.background_color
self.background_color = color
width = _CFG["canvwidth"]
height = _CFG["canvheight"]
if self.mode() in ["logo", "standard"]:
x = -width // 2
y = -height // 2
else:
x = 0
y = -height
self.frame_index += 1
rect = SVG.rect(x=x, y=y, width=width, height=height, fill=color)
if self._animate:
rect.setAttribute("style", "display: none;")
an = SVG.animate(
Id=self.animation_frame_id(self.frame_index),
attributeName="display",
attributeType="CSS",
From="block",
to="block",
dur=_CFG["min_duration"],
fill="freeze",
)
an.setAttribute(
"begin", self.animation_frame_id(self.frame_index - 1) + ".end"
)
appendTo(rect, an)
appendTo(self.background_canvas, rect)
def _convert_coordinates(self, x, y):
"""In the browser, the increasing y-coordinate is towards the
bottom of the screen; this is the opposite of what is assumed
normally for the methods in the CPython turtle module.
This method makes the necessary orientation. It should be called
just prior to creating any SVG element.
"""
return x * self.yscale, self.y_points_down * y * self.yscale
def create_svg_turtle(self, _turtle, name):
if name in self.shapes:
fn, arg = self.shapes[name]
else:
print("Unknown turtle '%s'; the default turtle will be used")
fn, arg = self.shapes[_CFG["shape"]]
shape = fn(arg)
if self._mode == "standard" or self._mode == "world":
rotation = -90
else:
rotation = 0
return shape, rotation
def _dot(self, pos, size, color):
"""Draws a filled circle of specified size and color"""
if color is None:
color = "black"
if size is None or size < 1:
size = 1
self.frame_index += 1
# `size` represents the diameter, svg needs the radius
radius = size / 2
x, y = self._convert_coordinates(pos[0], pos[1])
circle = SVG.circle(cx=x, cy=y, r=radius, fill=color)
if self._animate:
circle.setAttribute("style", "display: none;")
an = SVG.animate(
Id=self.animation_frame_id(self.frame_index),
attributeName="display",
attributeType="CSS",
From="block",
to="block",
dur=_CFG["min_duration"],
fill="freeze",
)
an.setAttribute(
"begin", self.animation_frame_id(self.frame_index - 1) + ".end"
)
appendTo(circle, an)
appendTo(self.canvas, circle)
def _drawline(self, _turtle, coordlist=None, color=None, width=1, speed=None):
"""Draws an animated line with a turtle
- coordlist is the egin and end coordinates of the line
- color should include the current outline and fill colors;
- width is width of line to be drawn.
- speed is the animation speed
"""
outline = color[0]
fill = color[1]
x0, y0 = coordlist[0]
x1, y1 = coordlist[1]
x0, y0 = self._convert_coordinates(x0, y0)
x1, y1 = self._convert_coordinates(x1, y1)
# The speed scale does not correspond exactly to the CPython one...
if speed == 0:
duration = _CFG["min_duration"]
else:
dist = _turtle._distance
if speed is None or speed == 1:
duration = 0.02 * dist
else:
duration = 0.02 * dist / speed**1.2
if duration < 0.001:
duration = _CFG["min_duration"]
else:
duration = "%6.3fs" % duration
drawing = _turtle._drawing
style = {"stroke": outline, "stroke-width": width}
if self._animate:
_line = SVG.line(x1=x0, y1=y0, x2=x0, y2=y0, style=style)
else:
_line = SVG.line(x1=x0, y1=y0, x2=x1, y2=y1, style=style)
if not drawing:
_line.setAttribute("opacity", 0)
# always create one animation for timing purpose
begin = self.animation_frame_id(self.frame_index) + ".end"
self.frame_index += 1
if self._animate:
_an1 = SVG.animate(
Id=self.animation_frame_id(self.frame_index),
attributeName="x2",
attributeType="XML",
From=x0,
to=x1,
dur=duration,
fill="freeze",
begin=begin,
)
appendTo(_line, _an1)
# But, do not bother adding animations that will not be shown.
if drawing:
if self._animate:
_an2 = SVG.animate(
attributeName="y2",
attributeType="XML",
begin=begin,
From=y0,
to=y1,
dur=duration,
fill="freeze",
)
appendTo(_line, _an2)
if width > 2:
if self._animate:
_line_cap = SVG.set(
attributeName="stroke-linecap",
begin=begin,
attributeType="xml",
to="round",
dur=duration,
fill="freeze",
)
appendTo(_line, _line_cap)
else:
_line.setAttribute("stroke-linecap", "round")
appendTo(self.canvas, _line)
return begin, duration, (x0, y0), (x1, y1)
def _drawpoly(self, coordlist, outline=None, fill=None, width=None):
"""Draws a path according to provided arguments:
- coordlist is sequence of coordinates
- fill is filling color
- outline is outline color
- width is the outline width
"""
self.frame_index += 1
if self._animate:
style = {"display": "none"}
else:
style = {"display": "block"}
if fill is not None:
style["fill"] = fill
if outline is not None:
style["stroke"] = outline
if width is not None:
style["stroke-width"] = width
else:
style["stroke-width"] = 1
points = " ".join(
",".join(map(str, self._convert_coordinates(*p))) for p in coordlist
)
polygon = SVG.polygon(points=points, style=style)
if self._animate:
an = SVG.animate(
Id=self.animation_frame_id(self.frame_index),
attributeName="display",
attributeType="CSS",
From="block",
to="block",
dur=_CFG["min_duration"],
fill="freeze",
)
an.setAttribute(
"begin", self.animation_frame_id(self.frame_index - 1) + ".end"
)
appendTo(polygon, an)
appendTo(self.canvas, polygon)
def _new_frame(self):
"""returns a new animation frame index and update the current index"""
previous_end = self.animation_frame_id(self.frame_index) + ".end"
self.frame_index += 1
new_frame_id = self.animation_frame_id(self.frame_index)
return previous_end, new_frame_id
def mode(self, _mode=None):
if _mode is None:
return self._mode
_CFG["mode"] = _mode
self.reset()
def reset(self):
self._turtles = []
self.frame_index = 0
self.background_color = "white"
self._scene_finished = False
self._set_geometry()
def restart(self):
_CFG.update(_default_cfg())
self.reset()
Turtle._pen = None
def _set_geometry(self):
self.width = _CFG["canvwidth"]
self.height = _CFG["canvheight"]
self.x_offset = self.y_offset = 0
self.xscale = self.yscale = 1
self.y_points_down = -1
self._mode = _CFG["mode"].lower()
if self._mode in ["logo", "standard"]:
self.translate_canvas = (self.width // 2, self.height // 2)
elif self._mode == "world":
self.translate_canvas = (0, self.height)
self._setup_canvas()
def _setup_canvas(self):
self.svg_id = generate_id()
self.svg_scene = SVG.svg(
width=self.width,
height=self.height,
preserveAspectRatio="xMidYMid meet",
viewBox="0 0 {} {}".format(self.width, self.height),
)
translate = "translate(%d %d)" % self.translate_canvas
# always create one animation for timing purpose
# if _animate is False, we remove it in end_scene()
self._timing_anim = SVG.animate(
Id=self.animation_frame_id(self.frame_index),
attributeName="opacity",
attributeType="CSS",
From=1,
to=1,
begin="0s",
dur=_CFG["min_duration"],
fill="freeze",
)
appendTo(self.svg_scene, self._timing_anim)
# Unlike html elements, svg elements have no concept of a z-index: each
# new element is drawn on top of each other.
# Having separate canvas keeps the ordering
self.background_canvas = SVG.g(transform=translate)
self.canvas = SVG.g(transform=translate)
self.writing_canvas = SVG.g(transform=translate)
self.turtle_canvas = SVG.g(transform=translate)
appendTo(self.svg_scene, self.background_canvas)
appendTo(self.svg_scene, self.canvas)
appendTo(self.svg_scene, self.writing_canvas)
appendTo(self.svg_scene, self.turtle_canvas)
def setworldcoordinates(self, llx, lly, urx, ury):
"""Set up a user defined coordinate-system.
Arguments:
llx -- a number, x-coordinate of lower left corner of canvas
lly -- a number, y-coordinate of lower left corner of canvas
urx -- a number, x-coordinate of upper right corner of canvas
ury -- a number, y-coordinate of upper right corner of canvas
Note: llx must be less than urx in this version.
Warning: in user-defined coordinate systems angles may appear distorted.
"""
self._mode = "world"
if urx < llx:
sys.stderr.write(
"Warning: urx must be greater than llx; your choice will be reversed"
)
urx, llx = llx, urx
xspan = urx - llx
yspan = abs(ury - lly)
self.xscale = int(self.width) / xspan
self.yscale = int(self.height) / yspan
self.x_offset = -llx * self.xscale
if ury < lly:
self.y_points_down = 1 # standard orientation in the browser
else:
self.y_points_down = -1
self.y_offset = self.y_points_down * lly * self.yscale
self.translate_canvas = (self.x_offset, self.height - self.y_offset)
self._setup_canvas()
def end_scene(self):
"""Ends the creation of a "scene" and has it displayed"""
# removing animation used for timing purpose
if not self._animate and self._timing_anim is not None:
self.svg_scene.removeChild(self._timing_anim)
self._timing_anim = None
if not self._scene_finished:
for t in self._turtles:
appendTo(self.turtle_canvas, t.svg)
self._scene_finished = True
self._old_svg_scene = self.svg_scene
return self.svg_scene
def show_scene(self):
"""Ends the creation of a "scene" and has it displayed"""
return self.end_scene()
def turtles(self):
"""Return the list of turtles on the screen."""
return self._turtles
def _write(self, pos, txt, align, font, color):
"""Write txt at pos in canvas with specified font
and color."""
if isinstance(color, tuple):
stroke = color[0]
fill = color[1]
else:
fill = color
stroke = None
x, y = self._convert_coordinates(pos[0], pos[1])
text = SVG.text(
txt,
x=x,
y=y,
fill=fill,
style={
"display": "none" if self._animate else "block",
"font-family": font[0],
"font-size": font[1],
"font-style": font[2],
},
)
if stroke is not None:
text.setAttribute("stroke", stroke)
if align == "left":
text.setAttribute("text-anchor", "start")
elif align == "center" or align == "centre":
text.setAttribute("text-anchor", "middle")
elif align == "right":
text.setAttribute("text-anchor", "end")
self.frame_index += 1
if self._animate:
an = SVG.animate(
Id=self.animation_frame_id(self.frame_index),
attributeName="display",
attributeType="CSS",
From="block",
to="block",
dur=_CFG["min_duration"],
fill="freeze",
)
an.setAttribute(
"begin", self.animation_frame_id(self.frame_index - 1) + ".end"
)
appendTo(text, an)
appendTo(self.writing_canvas, text)
def addshape(self, *args, **kwargs):
sys.stderr.write("Warning: Screen.addshape() is not implemented.\n")
def bgpic(self, *args, **kwargs):
sys.stderr.write("Warning: Screen.bgpic() is not implemented.\n")
def bye(self, *args, **kwargs):
sys.stderr.write("Warning: Screen.bye() is not implemented.\n")
def clearscreen(self, *args, **kwargs):
sys.stderr.write("Warning: Screen.clearscreen() is not implemented.\n")
def colormode(self, *args, **kwargs):
sys.stderr.write("Warning: Screen.colormode() is not implemented.\n")
def delay(self, *args, **kwargs):
sys.stderr.write("Warning: Screen.delay() is not implemented.\n")
def exitonclick(self, *args, **kwargs):
sys.stderr.write("Warning: Screen.exitonclick() is not implemented.\n")
def getcanvas(self, *args, **kwargs):
sys.stderr.write("Warning: Screen.getcanvas() is not implemented.\n")
def getshapes(self, *args, **kwargs):
sys.stderr.write("Warning: Screen.getshapes() is not implemented.\n")
def listen(self, *args, **kwargs):
sys.stderr.write("Warning: Screen.listen() is not implemented.\n")
def numinput(self, *args, **kwargs):
sys.stderr.write("Warning: Screen.numinput() is not implemented.\n")
def onkey(self, *args, **kwargs):
sys.stderr.write("Warning: Screen.onkey() is not implemented.\n")
def onkeypress(self, *args, **kwargs):
sys.stderr.write("Warning: Screen.onkeypress() is not implemented.\n")
def onkeyrelease(self, *args, **kwargs):
sys.stderr.write("Warning: Screen.onkeyrelease() is not implemented.\n")
def onscreenclick(self, *args, **kwargs):
sys.stderr.write("Warning: Screen.onscreenclick() is not implemented.\n")
def ontimer(self, *args, **kwargs):
sys.stderr.write("Warning: Screen.ontimer() is not implemented.\n")
def register_shape(self, *args, **kwargs):
sys.stderr.write("Warning: Screen.register_shape() is not implemented.\n")
def resetscreen(self, *args, **kwargs):
sys.stderr.write("Warning: Screen.resetscreen() is not implemented.\n")
def screensize(self, *args, **kwargs):
sys.stderr.write("Warning: Screen.screensize() is not implemented.\n")
def setup(self, *args, **kwargs):
sys.stderr.write("Warning: Screen.setup() is not implemented.\n")
def textinput(self, *args, **kwargs):
sys.stderr.write("Warning: Screen.textinput() is not implemented.\n")
def title(self, *args, **kwargs):
sys.stderr.write("Warning: Screen.title() is not implemented.\n")
def tracer(self, *args, **kwargs):
sys.stderr.write("Warning: Screen.tracer() is not implemented.\n")
def update(self, *args, **kwargs):
sys.stderr.write("Warning: Screen.update() is not implemented.\n")
def window_height(self, *args, **kwargs):
sys.stderr.write("Warning: Screen.window_height() is not implemented.\n")
def window_width(self, *args, **kwargs):
sys.stderr.write("Warning: Screen.window_width() is not implemented.\n")
class TNavigator:
"""Navigation part of the Turtle.
Implements methods for turtle movement.
"""
# START_ORIENTATION = {
# "standard": Vec2D(1.0, 0.0),
# "world": Vec2D(1.0, 0.0),
# "logo": Vec2D(0.0, 1.0)}
DEFAULT_MODE = "standard"
DEFAULT_ANGLEOFFSET = 0
DEFAULT_ANGLEORIENT = 1
def __init__(self, mode=DEFAULT_MODE):
self._angleOffset = self.DEFAULT_ANGLEOFFSET
self._angleOrient = self.DEFAULT_ANGLEORIENT
self._mode = mode
self.degree_to_radians = math.pi / 180
self.degrees()
self._mode = _CFG["mode"]
self._setmode(mode)
TNavigator.reset(self)
def reset(self):
"""reset turtle navigation to its initial values
The derived class, which will call it directly and add its own
"""
self._position = (0.0, 0.0)
self._x = 0
self._y = 0
self._angle = 0
self._old_heading = 0
def _setmode(self, mode=None):
"""Set turtle-mode to 'standard', 'world' or 'logo'."""
if mode is None:
return self._mode
if mode not in ["standard", "logo", "world"]:
print(mode, "is an unknown mode; it will be ignored.")
return
self._mode = mode
if mode in ["standard", "world"]:
self._angleOffset = 0
self._angleOrient = 1
else: # mode == "logo":
self._angleOffset = -self._fullcircle / 4.0
self._angleOrient = 1
def _setDegreesPerAU(self, fullcircle):
"""Helper function for degrees() and radians()"""
self._fullcircle = fullcircle
self._degreesPerAU = 360 / fullcircle
def degrees(self, fullcircle=360.0):
"""Set angle measurement units to degrees, or possibly other system."""
self._setDegreesPerAU(fullcircle)
def radians(self):
"""Set the angle measurement units to radians."""
self._setDegreesPerAU(2 * math.pi)
def _rotate(self, angle):
"""Turn turtle counterclockwise by specified angle if angle > 0."""
pass
def _goto(self, x, y):
pass # implemented by derived class
def forward(self, distance):
"""Move the turtle forward by the specified distance."""
x1 = distance * cos(self._angle * self.degree_to_radians)
y1 = distance * sin(self._angle * self.degree_to_radians)
self._distance = distance
self._goto(self._x + x1, self._y + y1)
fd = forward
def back(self, distance):
"""Move the turtle backward by distance."""
x1 = -distance * cos(self._angle * self.degree_to_radians)
y1 = -distance * sin(self._angle * self.degree_to_radians)
self._distance = distance
self._goto(self._x + x1, self._y + y1)
backward = back
bk = back
def right(self, angle):
"""Turn turtle right by angle units."""
angle *= self._degreesPerAU
self._angle += self.screen.y_points_down * angle
self._rotate_image(-angle)
rt = right
def left(self, angle):
"""Turn turtle left by angle units."""
angle *= self._degreesPerAU
self._angle += -self.screen.y_points_down * angle
self._rotate_image(angle)
lt = left
def pos(self):
"""Return the turtle's current location (x,y), as a formatted tuple"""
return Vec2D((self._x, self._y))
position = pos
def xcor(self):
"""Return the turtle's x coordinate."""
return self._x
def ycor(self):
"""Return the turtle's y coordinate"""
return self._y
def goto(self, x, y=None):
"""Move turtle to an absolute position."""
if y is None:
x, y = x[0], x[1] # "*x" here raises SyntaxError
# distance only needed to calculate the duration of
# the animation which is based on "distance" and "speed" as well.
# We use the Manhattan distance here as it is *much* faster on Chrome,
# than using the proper distance with calls to math.sqrt, while
# giving acceptable results
#
# forward, backward, etc., call _goto directly with the distance
# given by the user
self._distance = abs(self._x - x) + abs(self._y - y)
self._goto(x, y)
setpos = goto
setposition = goto
def home(self):
"""Move turtle to the origin - coordinates (0,0), facing in the
default orientation
"""
self.goto(0, 0)
self.setheading(0)
def setx(self, x):
"""Set the turtle's first coordinate to x"""
self._distance = abs(x - self._x)
self._goto(x, self._y)
def sety(self, y):
"""Set the turtle's second coordinate to y"""
self._distance = abs(y - self._y)
self._goto(self._x, y)
def distance(self, x, y=None):
"""
Return the distance from the turtle to (x,y) in turtle step units.
"""
if y is None:
assert isinstance(x, tuple)
x, y = x
return math.sqrt((self._x - x) ** 2 + (self._y - y) ** 2)
def towards(self, x, y=None):
"""
Return the angle of the line from the turtle's position to (x, y).
"""
if y is None:
assert isinstance(x, tuple)
x, y = x
x, y = x - self._x, y - self._y
result = round(math.atan2(y, x) * 180.0 / math.pi, 10) % 360.0
result /= self._degreesPerAU
return (self._angleOffset + self._angleOrient * result) % self._fullcircle
def heading(self):
"""Return the turtle's current heading."""
angle = self._angle / self._degreesPerAU
return (self._angleOffset + self._angleOrient * angle) % self._fullcircle
def setheading(self, to_angle):
"""Set the orientation of the turtle to to_angle."""
rot = min((to_angle + i * 360 - self._angle for i in range(-2, 3)), key=abs)
self._rotate(rot)
seth = setheading
def circle(self, radius, extent=None, steps=None):
"""
Draw an approximate (arc) circle with given radius, using straight
line segments.
Arguments:
radius -- a number
extent (optional) -- a number
steps (optional) -- an integer
Draw a circle with given radius. The center is radius units left
of the turtle; extent - an angle - determines which part of the
circle is drawn. If extent is not given, draw the entire circle.
If extent is not a full circle, one endpoint of the arc is the
current pen position. Draw the arc in counterclockwise direction
if radius is positive, otherwise in clockwise direction. Finally
the direction of the turtle is changed by the amount of extent.
As the circle is approximated by an inscribed regular polygon,
steps determines the number of steps to use. If not given,
it will be calculated automatically. Maybe used to draw regular
polygons.
"""
speed = self.speed()
if extent is None:
extent = self._fullcircle
if steps is None:
frac = abs(extent) / self._fullcircle
steps = 1 + int(min(11 + abs(radius) / 6.0, 59.0) * frac)
w = 1.0 * extent / steps
w2 = 0.5 * w
l = 2.0 * radius * math.sin(w2 * math.pi / 180.0 * self._degreesPerAU)
if radius < 0:
l, w, w2 = -l, -w, -w2
self._rotate(w2)
for i in range(steps):
self.speed(speed)
self.forward(l)
self.speed(0)
self._rotate(w)
self._rotate(-w2)
self.speed(speed)
class TPen:
"""Drawing part of the Turtle."""
def __init__(self):
self.screen = Screen()
self._reset()
def _reset(self, pencolor=_CFG["pencolor"], fillcolor=_CFG["fillcolor"]):
self._pensize = 1
self._shown = True
self._drawing = True
self._pencolor = "black"
self._fillcolor = "black"
self._speed = 3
self._stretchfactor = (1.0, 1.0)
def resizemode(self, rmode=None):
sys.stderr.write("Warning: TPen.resizemode() is not implemented.\n")
def pensize(self, width=None):
"""Set or return the line thickness."""
if width is None:
return self._pensize
self.pen(pensize=width)
width = pensize
def pendown(self):
"""Pull the pen down -- drawing when moving."""
if self._drawing:
return
self.pen(pendown=True)
pd = pendown
down = pendown
def penup(self):
"""Pull the pen up -- no drawing when moving."""
if not self._drawing:
return
self.pen(pendown=False)
pu = penup
up = penup
def isdown(self):
"""Return True if pen is down, False if it's up."""
return self._drawing
def speed(self, speed=None):
"""Return or set the turtle's speed.
Optional argument:
speed -- an integer in the range 0..10 or a speedstring (see below)
Set the turtle's speed to an integer value in the range 0 .. 10.
If no argument is given: return current speed.
If input is a number greater than 10 or smaller than 0.5,
speed is set to 0.
Speedstrings are mapped to speedvalues in the following way:
'fastest' : 0
'fast' : 10
'normal' : 6
'slow' : 3
'slowest' : 1
speeds from 1 to 10 enforce increasingly faster animation of
line drawing and turtle turning.
Attention:
speed = 0 : *no* animation takes place. forward/back makes turtle jump
and likewise left/right make the turtle turn instantly.
"""
speeds = {
"fastest": 0,
"fast": 10,
"normal": 6,
"slow": 3,
"slowest": 1,
}
if speed is None:
return self._speed
if speed in speeds:
speed = speeds[speed]
elif 0.5 < speed < 10.5:
speed = int(round(speed))
else:
speed = 0
self.pen(speed=speed)
def color(self, *args):
"""Return or set the pencolor and fillcolor.
IMPORTANT: this is very different than the CPython's version.
Colors are using strings in any format recognized by a browser
(named color, rgb, rgba, hex, hsl, etc.)
Acceptable arguments:
no argument: returns (pencolor, fillcolor)
single string -> sets both pencolor and fillcolor to that value
two string arguments -> taken to be pencolor, fillcolor
tuple of two strings -> taken to be (pencolor, fillcolor)
"""
if args:
pencolor, fillcolor = None, None
l = len(args)
if l == 1:
if isinstance(args[0], tuple):
pencolor = args[0][0]
fillcolor = args[0][1]
else:
pencolor = fillcolor = args[0]
elif l == 2:
pencolor, fillcolor = args
if not isinstance(pencolor, str) or not isinstance(fillcolor, str):
raise TurtleGraphicsError("bad color arguments: %s" % str(args))
self.pen(pencolor=pencolor, fillcolor=fillcolor)
else:
return self._pencolor, self._fillcolor
def pencolor(self, color=None):
"""Return or set the pencolor.
IMPORTANT: this is very different than the CPython's version.
Colors are using strings in any format recognized by a browser
(named color, rgb, rgba, hex, hsl, etc.)
"""
if color is not None:
if not isinstance(color, str):
raise TurtleGraphicsError("bad color arguments: %s" % str(color))
if color == self._pencolor:
return
self.pen(pencolor=color)
else:
return self._pencolor
def fillcolor(self, color=None):
"""Return or set the fillcolor.
IMPORTANT: this is very different than the CPython's version.
Colors are using strings in any format recognized by a browser
(named color, rgb, rgba, hex, hsl, etc.)
"""
if color is not None:
if not isinstance(color, str):
raise TurtleGraphicsError("bad color arguments: %s" % str(color))
if color == self._fillcolor:
return
self.pen(fillcolor=color)
else:
return self._pencolor
def showturtle(self):
"""Makes the turtle visible."""
if self._shown:
return
self.pen(shown=True)
self.left(0) # this will update the display to the correct rotation
st = showturtle
def hideturtle(self):
"""Makes the turtle invisible."""
if self._shown:
self.pen(shown=False)
ht = hideturtle
def isvisible(self):
"""Return True if the Turtle is shown, False if it's hidden."""
return self._shown
def pen(self, pen=None, **pendict):
"""Return or set the pen's attributes.
Arguments:
pen -- a dictionary with some or all of the below listed keys.
**pendict -- one or more keyword-arguments with the below
listed keys as keywords.
Return or set the pen's attributes in a 'pen-dictionary'
with the following key/value pairs:
"shown" : True/False
"pendown" : True/False
"pencolor" : color-string or color-tuple
"fillcolor" : color-string or color-tuple
"pensize" : positive number
"speed" : number in range 0..10
"""
_pd = {
"shown": self._shown,
"pendown": self._drawing,
"pencolor": self._pencolor,
"fillcolor": self._fillcolor,
"pensize": self._pensize,
"speed": self._speed,
}
if not (pen or pendict):
return _pd
if isinstance(pen, dict):
p = pen
else:
p = {}
p.update(pendict)
_p_buf = {}
for key in p:
_p_buf[key] = _pd[key]
if "pendown" in p:
self._drawing = p["pendown"]
if "pencolor" in p:
old_color = self._pencolor
self._pencolor = p["pencolor"]
previous_end, new_frame_id = self.screen._new_frame()
if self.screen._animate:
anim = SVG.animate(
Id=new_frame_id,
begin=previous_end,
dur=_CFG["min_duration"],
fill="freeze",
attributeName="stroke",
attributeType="XML",
From=old_color,
to=self._pencolor,
)
appendTo(self.svg, anim)
else:
self.svg.setAttribute("stroke", self._pencolor)
if "pensize" in p:
self._pensize = p["pensize"]
if "fillcolor" in p:
old_color = self._fillcolor
self._fillcolor = p["fillcolor"]
previous_end, new_frame_id = self.screen._new_frame()
if self.screen._animate:
anim = SVG.animate(
Id=new_frame_id,
begin=previous_end,
dur=_CFG["min_duration"],
fill="freeze",
attributeName="fill",
attributeType="XML",
From=old_color,
to=self._fillcolor,
)
appendTo(self.svg, anim)
else:
self.svg.setAttribute("fill", self._fillcolor)
if "speed" in p:
self._speed = p["speed"]
if "shown" in p:
old_shown = self._shown
if old_shown:
opacity = 0
old_opacity = 1
else:
opacity = 1
old_opacity = 0
previous_end, new_frame_id = self.screen._new_frame()
if self.screen._animate:
anim = SVG.animate(
Id=new_frame_id,
begin=previous_end,
dur=_CFG["min_duration"],
fill="freeze",
attributeName="opacity",
attributeType="XML",
From=old_opacity,
to=opacity,
)
appendTo(self.svg, anim)
else:
self.svg.setAttribute("opacity", opacity)
self.forward(0) # updates the turtle visibility on screen
self._shown = p["shown"]
# No RawTurtle/RawPen for this version, unlike CPython's; only Turtle/Pen
class Turtle(TPen, TNavigator):
"""Animation part of the Turtle.
Puts Turtle upon a TurtleScreen and provides tools for
its animation.
"""
_pen = None
screen = None
def __init__(self, shape=_CFG["shape"], visible=_CFG["visible"]):
self.screen = Screen()
TPen.__init__(self)
TNavigator.__init__(self, self.screen.mode())
self._poly = None
self._creatingPoly = False
self._fillitem = self._fillpath = None
self.name = shape
self.svg, rotation = self.screen.create_svg_turtle(self, name=shape)
self.svg.setAttribute("opacity", 0)
self._shown = False
if visible:
self.showturtle() # will ensure that turtle become visible at appropriate time
self.screen._turtles.append(self)
self.rotation_correction = rotation
# apply correction to image orientation
self._old_heading = self.heading() + self.rotation_correction
speed = self.speed()
self.speed(0)
self.left(
-self._angleOffset
) # this will update the display to include the correction
self.speed(speed)
def reset(self):
"""Delete the turtle's drawings and restore its default values."""
# TODO: review this and most likely revise docstring.
TNavigator.reset(self)
TPen._reset(self)
self._old_heading = self.heading() + self.rotation_correction
self.home()
self.color(_CFG["pencolor"], _CFG["fillcolor"])
def clear(self):
sys.stderr.write("Warning: Turtle.clear() is not implemented.\n")
def shape(self, name=None):
"""Set turtle shape to shape with given name
/ return current shapename if no name is provided
"""
if name is None:
return self.name
_turtle = self._make_copy(name=name)
visible = self.isvisible()
if visible:
self.hideturtle()
appendTo(self.screen.turtle_canvas, self.svg)
self.svg = _turtle
self.screen._turtles.append(self)
if visible:
self.showturtle()
def clearstamp(self, *args, **kwargs):
sys.stderr.write("Warning: Turtle.clearstamp() is not implemented.\n")
def clearstamps(self, *args, **kwargs):
sys.stderr.write("Warning: Turtle.clearstamps() is not implemented.\n")
def onclick(self, *args, **kwargs):
sys.stderr.write("Warning: Turtle.onclick() is not implemented.\n")
def ondrag(self, *args, **kwargs):
sys.stderr.write("Warning: Turtle.ondrag() is not implemented.\n")
def onrelease(self, *args, **kwargs):
sys.stderr.write("Warning: Turtle.onrelease() is not implemented.\n")
def undo(self, *args, **kwargs):
sys.stderr.write("Warning: Turtle.undo() is not implemented.\n")
def setundobuffer(self, *args, **kwargs):
sys.stderr.write("Warning: Turtle.setundobuffer() is not implemented.\n")
def undobufferentries(self, *args, **kwargs):
sys.stderr.write("Warning: Turtle.undobufferentries() is not implemented.\n")
def shapesize(self, *args, **kwargs):
sys.stderr.write("Warning: Turtle.shapesize() is not implemented.\n")
turtlesize = shapesize
def shearfactor(self, shear=None):
sys.stderr.write("Warning: Turtle.shearfactor() is not implemented.\n")
def settiltangle(self, angle):
sys.stderr.write("Warning: Turtle.settiltangle() is not implemented.\n")
def tiltangle(self, angle=None):
sys.stderr.write("Warning: Turtle.tiltangle() is not implemented.\n")
def tilt(self, angle):
sys.stderr.write("Warning: Turtle.tilt() is not implemented.\n")
def shapetransform(self, t11=None, t12=None, t21=None, t22=None):
sys.stderr.write("Warning: Turtle.shapetransform() is not implemented.\n")
def get_shapepoly(self):
sys.stderr.write("Warning: Turtle.get_shapepoly() is not implemented.\n")
def _goto(self, x, y):
"""Move the pen to the point end, thereby drawing a line
if pen is down. All other methods for turtle movement depend
on this one.
"""
begin, duration, _from, _to = self.screen._drawline(
self,
((self._x, self._y), (x, y)),
(self._pencolor, self._fillcolor),
self._pensize,
self._speed,
)
if self._shown:
if self.screen._animate:
appendTo(
self.svg,
SVG.animateMotion(
begin=begin, dur=_CFG["min_duration"], fill="remove"
),
)
appendTo(
self.svg,
SVG.animateMotion(
From="%s,%s" % _from,
to="%s,%s" % _to,
dur=duration,
begin=begin,
fill="freeze",
),
)
else:
self.svg.setAttribute(
"transform",
f"translate({_to[0]}, {_to[1]}) rotate({self._old_heading}, 0, 0)",
)
if self._fillpath is not None:
self._fillpath.append((x, y))
self._position = (x, y)
self._x = x
self._y = y
def _rotate(self, angle):
"""Turns pen clockwise by angle."""
angle *= self._degreesPerAU
self._angle += -self.screen.y_points_down * angle
self._rotate_image(angle)
def _rotate_image(self, angle):
new_heading = self._old_heading - angle
if self.isvisible():
previous_end, new_frame_id = self.screen._new_frame()
if self._speed == 0:
duration = _CFG["min_duration"]
else:
duration = abs(angle) / (self._speed * 360)
if duration < 0.001:
duration = _CFG["min_duration"]
else:
duration = "%6.3fs" % duration
if self.screen._animate:
appendTo(
self.svg,
SVG.animateMotion(
begin=previous_end,
dur=_CFG["min_duration"],
fill="remove",
),
)
appendTo(
self.svg,
SVG.animateTransform(
attributeName="transform",
Id=new_frame_id,
type="rotate",
From=f"{self._old_heading},0,0",
to=f"{new_heading},0,0",
begin=previous_end,
dur=duration,
fill="freeze",
),
)
else:
x, y = self.screen._convert_coordinates(self._x, self._y)
self.svg.setAttribute(
"transform",
f"translate({x}, {y}) rotate({new_heading}, 0, 0)",
)
self._old_heading = new_heading
def filling(self):
"""Return fillstate (True if filling, False else)."""
return self._fillpath is not None
def begin_fill(self):
"""Called just before drawing a shape to be filled."""
self._fillpath = [(self._x, self._y)]
def end_fill(self):
"""Fill the shape drawn after the call begin_fill()."""
if self.filling() and len(self._fillpath) > 2:
self.screen._drawpoly(
self._fillpath,
outline=self._pencolor,
fill=self._fillcolor,
)
else:
print("No path to fill.")
self._fillpath = None
def dot(self, size=None, color=None):
"""Draw a filled circle with diameter size, using color."""
if size is None:
size = max(self._pensize + 4, 2 * self._pensize)
if color is None:
color = self._pencolor
item = self.screen._dot((self._x, self._y), size, color=color)
def _write(self, txt, align, font, color=None):
"""Performs the writing for write()"""
if color is None:
color = self._pencolor
self.screen._write((self._x, self._y), txt, align, font, color)
def write(self, arg, align="left", font=("Arial", 8, "normal"), color=None):
"""Write text at the current turtle position.
Arguments:
arg -- info, which is to be written to the TurtleScreen; it will be
converted to a string.
align (optional) -- one of the strings "left", "center" or right"
font (optional) -- a triple (fontname, fontsize, fonttype)
"""
self._write(str(arg), align.lower(), font, color=color)
def begin_poly(self):
"""Start recording the vertices of a polygon."""
self._poly = [(self._x, self._y)]
self._creatingPoly = True
def end_poly(self):
"""Stop recording the vertices of a polygon."""
self._creatingPoly = False
def get_poly(self):
"""Return the lastly recorded polygon."""
# check if there is any poly?
if self._poly is not None:
return tuple(self._poly)
def getscreen(self):
"""Return the TurtleScreen object, the turtle is drawing on."""
return self.screen
def getturtle(self):
"""Return the Turtle object itself.
Only reasonable use: as a function to return the 'anonymous turtle'
"""
return self
getpen = getturtle
def _make_copy(self, name=None):
"""makes a copy of the current svg turtle, but possibly using a
different shape. This copy is then ready to be inserted
into a canvas."""
if name is None:
name = self.name
# We recreate a copy of the existing turtle, possibly using a different
# name/shape; we set the opacity to
# 0 since there is no specific time associated with the creation of
# such an object: we do not want to show it early.
_turtle, rotation = self.screen.create_svg_turtle(self, name=name)
_turtle.setAttribute("opacity", 0 if self.screen._animate else 1)
_turtle.setAttribute("fill", self._fillcolor)
_turtle.setAttribute("stroke", self._pencolor)
# We use timed animations to get it with the proper location,
# orientation and appear at the desired time.
previous_end, new_frame_id = self.screen._new_frame()
x, y = self.screen._convert_coordinates(self._x, self._y)
if self.screen._animate:
appendTo(
_turtle,
SVG.animateMotion(
begin=previous_end, dur=_CFG["min_duration"], fill="remove"
),
)
appendTo(
_turtle,
SVG.animateMotion(
Id=new_frame_id,
From="%s,%s" % (x, y),
to="%s,%s" % (x, y),
dur=_CFG["min_duration"],
begin=previous_end,
fill="freeze",
),
)
appendTo(
_turtle,
SVG.animateTransform(
attributeName="transform",
type="rotate",
From=f"{self._old_heading},0,0",
to=f"{self._old_heading},0,0",
begin=previous_end,
dur=_CFG["min_duration"],
fill="freeze",
),
)
appendTo(
_turtle,
SVG.animate(
begin=previous_end,
dur=_CFG["min_duration"],
fill="freeze",
attributeName="opacity",
attributeType="XML",
From=0,
to=1,
),
)
else:
_turtle.setAttribute(
"transform",
f"translate({x}, {y}) rotate({self._old_heading}, 0, 0)",
)
_turtle.setAttribute("opacity", "1")
return _turtle
def stamp(self):
"""draws a permanent copy of the turtle at its current location"""
_turtle = self._make_copy(name=self.name)
appendTo(self.screen.canvas, _turtle)
def clone(self):
"""Create and return a clone of the turtle."""
n = Turtle(self.name)
attrs = dir(self)
new_dict = {}
for attr in attrs:
if isinstance(getattr(self, attr), (int, str, float)):
new_dict[attr] = getattr(self, attr)
n.__dict__.update(**new_dict)
# ensure that visible characteristics are consistent with settings
if not n._shown:
n._shown = True # otherwise, hideturtle() would have not effect
n.hideturtle()
n.left(0)
n.fd(0)
n.color(n.color())
return n
Pen = Turtle
def done(target=None):
"""
If target_id is given, the SVG element containing the turtle output will
become a child of the HtmlElement with that id. If no element with that id
can be found, a ValueError is raised.
If no target_id is given (the default), the document body has a new child
appended to it.
"""
Screen().show_scene()
if target:
container = target
else:
import js
container = js.document.createElement("div")
js.document.body.appendChild(container)
container.innerHTML = svg()
show_scene = done
mainloop = done
def replay_scene():
"Start playing an animation by 'refreshing' the canvas."
sys.stderr.write("Warning: turtle.replay_scene() is not implemented.\n")
def restart():
"For Brython turtle: clears the existing drawing and canvas"
_CFG.update(_default_cfg())
Screen().reset()
Turtle._pen = None
# The following functions are auto-generated.
def back(distance):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.back(distance)
def backward(distance):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.backward(distance)
def begin_fill():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.begin_fill()
def begin_poly():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.begin_poly()
def bk(distance):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.bk(distance)
def circle(radius, extent=None, steps=None):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.circle(radius, extent, steps)
def clear():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.clear()
def clearstamp(*args, **kwargs):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.clearstamp(*args, **kwargs)
def clearstamps(*args, **kwargs):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.clearstamps(*args, **kwargs)
def clone():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.clone()
def color(*args):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.color(*args)
def degrees(fullcircle=360.0):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.degrees(fullcircle)
def distance(x, y=None):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.distance(x, y)
def dot(size=None, color=None):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.dot(size, color)
def down():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.down()
def end_fill():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.end_fill()
def end_poly():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.end_poly()
def fd(distance):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.fd(distance)
def fillcolor(color=None):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.fillcolor(color)
def filling():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.filling()
def forward(distance):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.forward(distance)
def get_poly():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.get_poly()
def getpen():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.getpen()
def getscreen():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.getscreen()
def get_shapepoly():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.get_shapepoly()
def getturtle():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.getturtle()
def goto(x, y=None):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.goto(x, y)
def heading():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.heading()
def hideturtle():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.hideturtle()
def home():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.home()
def ht():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.ht()
def isdown():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.isdown()
def isvisible():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.isvisible()
def left(angle):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.left(angle)
def lt(angle):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.lt(angle)
def onclick(*args, **kwargs):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.onclick(*args, **kwargs)
def ondrag(*args, **kwargs):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.ondrag(*args, **kwargs)
def onrelease(*args, **kwargs):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.onrelease(*args, **kwargs)
def pd():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.pd()
def pen(pen=None, **pendict):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.pen(pen, **pendict)
def pencolor(color=None):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.pencolor(color)
def pendown():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.pendown()
def pensize(width=None):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.pensize(width)
def penup():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.penup()
def pos():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.pos()
def position():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.position()
def pu():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.pu()
def radians():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.radians()
def right(angle):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.right(angle)
def reset():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.reset()
def resizemode(rmode=None):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.resizemode(rmode)
def rt(angle):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.rt(angle)
def seth(to_angle):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.seth(to_angle)
def setheading(to_angle):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.setheading(to_angle)
def setpos(x, y=None):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.setpos(x, y)
def setposition(x, y=None):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.setposition(x, y)
def settiltangle(angle):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.settiltangle(angle)
def setundobuffer(*args, **kwargs):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.setundobuffer(*args, **kwargs)
def setx(x):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.setx(x)
def sety(y):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.sety(y)
def shape(name=None):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.shape(name)
def shapesize(*args, **kwargs):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.shapesize(*args, **kwargs)
def shapetransform(t11=None, t12=None, t21=None, t22=None):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.shapetransform(t11, t12, t21, t22)
def shearfactor(shear=None):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.shearfactor(shear)
def showturtle():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.showturtle()
def speed(speed=None):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.speed(speed)
def st():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.st()
def stamp():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.stamp()
def tilt(angle):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.tilt(angle)
def tiltangle(angle=None):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.tiltangle(angle)
def towards(x, y=None):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.towards(x, y)
def turtlesize(*args, **kwargs):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.turtlesize(*args, **kwargs)
def undo(*args, **kwargs):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.undo(*args, **kwargs)
def undobufferentries(*args, **kwargs):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.undobufferentries(*args, **kwargs)
def up():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.up()
def width(width=None):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.width(width)
def write(arg, align="left", font=("Arial", 8, "normal"), color=None):
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.write(arg, align, font, color)
def xcor():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.xcor()
def ycor():
if Turtle._pen is None:
Turtle._pen = Turtle()
return Turtle._pen.ycor()
def addshape(*args, **kwargs):
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.addshape(*args, **kwargs)
def animation(onoff):
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.animation(onoff)
def bgcolor(color=None):
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.bgcolor(color)
def bgpic(*args, **kwargs):
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.bgpic(*args, **kwargs)
def bye(*args, **kwargs):
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.bye(*args, **kwargs)
def clearscreen(*args, **kwargs):
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.clearscreen(*args, **kwargs)
def colormode(*args, **kwargs):
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.colormode(*args, **kwargs)
def delay(*args, **kwargs):
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.delay(*args, **kwargs)
def exitonclick(*args, **kwargs):
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.exitonclick(*args, **kwargs)
def getcanvas(*args, **kwargs):
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.getcanvas(*args, **kwargs)
def getshapes(*args, **kwargs):
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.getshapes(*args, **kwargs)
def listen(*args, **kwargs):
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.listen(*args, **kwargs)
def mode(_mode=None):
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.mode(_mode)
def numinput(*args, **kwargs):
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.numinput(*args, **kwargs)
def onkey(*args, **kwargs):
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.onkey(*args, **kwargs)
def onkeypress(*args, **kwargs):
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.onkeypress(*args, **kwargs)
def onkeyrelease(*args, **kwargs):
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.onkeyrelease(*args, **kwargs)
def onscreenclick(*args, **kwargs):
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.onscreenclick(*args, **kwargs)
def ontimer(*args, **kwargs):
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.ontimer(*args, **kwargs)
def register_shape(*args, **kwargs):
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.register_shape(*args, **kwargs)
def resetscreen(*args, **kwargs):
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.resetscreen(*args, **kwargs)
def save(file):
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.save(file)
def screensize(*args, **kwargs):
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.screensize(*args, **kwargs)
def setup(*args, **kwargs):
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.setup(*args, **kwargs)
def setworldcoordinates(llx, lly, urx, ury):
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.setworldcoordinates(llx, lly, urx, ury)
def svg():
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.svg()
def textinput(*args, **kwargs):
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.textinput(*args, **kwargs)
def title(*args, **kwargs):
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.title(*args, **kwargs)
def tracer(*args, **kwargs):
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.tracer(*args, **kwargs)
def turtles():
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.turtles()
def update(*args, **kwargs):
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.update(*args, **kwargs)
def window_height(*args, **kwargs):
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.window_height(*args, **kwargs)
def window_width(*args, **kwargs):
if Turtle.screen is None:
Turtle.screen = Screen()
return Turtle.screen.window_width(*args, **kwargs)
__all__ = [
"addshape",
"animation",
"bgcolor",
"bgpic",
"bye",
"clearscreen",
"colormode",
"delay",
"exitonclick",
"getcanvas",
"getshapes",
"listen",
"mode",
"numinput",
"onkey",
"onkeypress",
"onkeyrelease",
"onscreenclick",
"ontimer",
"register_shape",
"resetscreen",
"save",
"screensize",
"setup",
"setworldcoordinates",
"svg",
"textinput",
"title",
"tracer",
"turtles",
"update",
"window_height",
"window_width",
"back",
"backward",
"begin_fill",
"begin_poly",
"bk",
"circle",
"clear",
"clearstamp",
"clearstamps",
"clone",
"color",
"degrees",
"distance",
"dot",
"down",
"end_fill",
"end_poly",
"fd",
"fillcolor",
"filling",
"forward",
"get_poly",
"getpen",
"getscreen",
"get_shapepoly",
"getturtle",
"goto",
"heading",
"hideturtle",
"home",
"ht",
"isdown",
"isvisible",
"left",
"lt",
"onclick",
"ondrag",
"onrelease",
"pd",
"pen",
"pencolor",
"pendown",
"pensize",
"penup",
"pos",
"position",
"pu",
"radians",
"right",
"reset",
"resizemode",
"rt",
"seth",
"setheading",
"setpos",
"setposition",
"settiltangle",
"setundobuffer",
"setx",
"sety",
"shape",
"shapesize",
"shapetransform",
"shearfactor",
"showturtle",
"speed",
"st",
"stamp",
"tilt",
"tiltangle",
"towards",
"turtlesize",
"undo",
"undobufferentries",
"up",
"width",
"write",
"xcor",
"ycor",
"done",
"mainloop",
"restart",
"replay_scene",
"Turtle",
"Screen",
]