mirror of
https://github.com/pyscript/pyscript.git
synced 2025-12-20 10:47:35 -05:00
2487 lines
71 KiB
Python
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",
|
|
]
|