Turtle graphics
This commit is contained in:
parent
3b5330ef2d
commit
b241b67b89
|
@ -0,0 +1,343 @@
|
|||
# LogoMation-like turtle graphics
|
||||
|
||||
from math import * # Also for export
|
||||
import Tkinter
|
||||
Tk = Tkinter
|
||||
Error = Exception
|
||||
|
||||
class RawPen:
|
||||
|
||||
def __init__(self, canvas):
|
||||
self._canvas = canvas
|
||||
self._items = []
|
||||
self._tracing = 1
|
||||
self.degrees()
|
||||
self.reset()
|
||||
|
||||
def degrees(self, fullcircle=360.0):
|
||||
self._fullcircle = fullcircle
|
||||
self._invradian = pi / (fullcircle * 0.5)
|
||||
|
||||
def radians(self):
|
||||
self.degrees(2.0*pi)
|
||||
|
||||
def reset(self):
|
||||
canvas = self._canvas
|
||||
width = canvas.winfo_width()
|
||||
height = canvas.winfo_height()
|
||||
if width <= 1:
|
||||
width = canvas['width']
|
||||
if height <= 1:
|
||||
height = canvas['height']
|
||||
self._origin = float(width)/2.0, float(height)/2.0
|
||||
self._position = self._origin
|
||||
self._angle = 0.0
|
||||
self._drawing = 1
|
||||
self._width = 1
|
||||
self._color = "black"
|
||||
self._filling = 0
|
||||
self._path = []
|
||||
self._tofill = []
|
||||
self.clear()
|
||||
canvas._root().tkraise()
|
||||
|
||||
def clear(self):
|
||||
self.fill(0)
|
||||
canvas = self._canvas
|
||||
items = self._items
|
||||
self._items = []
|
||||
for item in items:
|
||||
canvas.delete(item)
|
||||
|
||||
def tracer(self, flag):
|
||||
self._tracing = flag
|
||||
|
||||
def forward(self, distance):
|
||||
x0, y0 = start = self._position
|
||||
x1 = x0 + distance * cos(self._angle*self._invradian)
|
||||
y1 = y0 - distance * sin(self._angle*self._invradian)
|
||||
self._goto(x1, y1)
|
||||
|
||||
def backward(self, distance):
|
||||
self.forward(-distance)
|
||||
|
||||
def left(self, angle):
|
||||
self._angle = (self._angle + angle) % self._fullcircle
|
||||
|
||||
def right(self, angle):
|
||||
self.left(-angle)
|
||||
|
||||
def up(self):
|
||||
self._drawing = 0
|
||||
|
||||
def down(self):
|
||||
self._drawing = 1
|
||||
|
||||
def width(self, width):
|
||||
self._width = float(width)
|
||||
|
||||
def color(self, *args):
|
||||
if not args:
|
||||
raise Error, "no color arguments"
|
||||
if len(args) == 1:
|
||||
color = args[0]
|
||||
if type(color) == type(""):
|
||||
# Test the color first
|
||||
try:
|
||||
id = self._canvas.create_line(0, 0, 0, 0, fill=color)
|
||||
except Tk.TclError:
|
||||
raise Error, "bad color string: %s" % `color`
|
||||
self._color = color
|
||||
return
|
||||
try:
|
||||
r, g, b = color
|
||||
except:
|
||||
raise Error, "bad color sequence: %s" % `color`
|
||||
else:
|
||||
try:
|
||||
r, g, b = args
|
||||
except:
|
||||
raise Error, "bad color arguments: %s" % `args`
|
||||
assert 0 <= r <= 1
|
||||
assert 0 <= g <= 1
|
||||
assert 0 <= b <= 1
|
||||
x = 255.0
|
||||
y = 0.5
|
||||
self._color = "#%02x%02x%02x" % (int(r*x+y), int(g*x+y), int(b*x+y))
|
||||
|
||||
def write(self, arg, move=0):
|
||||
x, y = start = self._position
|
||||
x = x-1 # correction -- calibrated for Windows
|
||||
item = self._canvas.create_text(x, y,
|
||||
text=str(arg), anchor="sw",
|
||||
fill=self._color)
|
||||
self._items.append(item)
|
||||
if move:
|
||||
x0, y0, x1, y1 = self._canvas.bbox(item)
|
||||
self._goto(x1, y1)
|
||||
|
||||
def fill(self, flag):
|
||||
if self._filling:
|
||||
path = tuple(self._path)
|
||||
smooth = self._filling < 0
|
||||
if len(path) > 2:
|
||||
item = self._canvas._create('polygon', path,
|
||||
{'fill': self._color,
|
||||
'smooth': smooth})
|
||||
self._items.append(item)
|
||||
self._canvas.lower(item)
|
||||
if self._tofill:
|
||||
for item in self._tofill:
|
||||
self._canvas.itemconfigure(item, fill=self._color)
|
||||
self._items.append(item)
|
||||
self._path = []
|
||||
self._tofill = []
|
||||
self._filling = flag
|
||||
if flag:
|
||||
self._path.append(self._position)
|
||||
|
||||
def circle(self, radius, extent=None):
|
||||
if extent is None:
|
||||
extent = self._fullcircle
|
||||
x0, y0 = self._position
|
||||
xc = x0 - radius * sin(self._angle * self._invradian)
|
||||
yc = y0 - radius * cos(self._angle * self._invradian)
|
||||
if radius >= 0.0:
|
||||
start = self._angle - 90.0
|
||||
else:
|
||||
start = self._angle + 90.0
|
||||
extent = -extent
|
||||
if self._filling:
|
||||
if abs(extent) >= self._fullcircle:
|
||||
item = self._canvas.create_oval(xc-radius, yc-radius,
|
||||
xc+radius, yc+radius,
|
||||
width=self._width,
|
||||
outline="")
|
||||
self._tofill.append(item)
|
||||
item = self._canvas.create_arc(xc-radius, yc-radius,
|
||||
xc+radius, yc+radius,
|
||||
style="chord",
|
||||
start=start,
|
||||
extent=extent,
|
||||
width=self._width,
|
||||
outline="")
|
||||
self._tofill.append(item)
|
||||
if self._drawing:
|
||||
if abs(extent) >= self._fullcircle:
|
||||
item = self._canvas.create_oval(xc-radius, yc-radius,
|
||||
xc+radius, yc+radius,
|
||||
width=self._width,
|
||||
outline=self._color)
|
||||
self._items.append(item)
|
||||
item = self._canvas.create_arc(xc-radius, yc-radius,
|
||||
xc+radius, yc+radius,
|
||||
style="arc",
|
||||
start=start,
|
||||
extent=extent,
|
||||
width=self._width,
|
||||
outline=self._color)
|
||||
self._items.append(item)
|
||||
angle = start + extent
|
||||
x1 = xc + abs(radius) * cos(angle * self._invradian)
|
||||
y1 = yc - abs(radius) * sin(angle * self._invradian)
|
||||
self._angle = (self._angle + extent) % self._fullcircle
|
||||
self._position = x1, y1
|
||||
if self._filling:
|
||||
self._path.append(self._position)
|
||||
|
||||
def goto(self, *args):
|
||||
if len(args) == 1:
|
||||
try:
|
||||
x, y = args[0]
|
||||
except:
|
||||
raise Error, "bad point argument: %s" % `args[0]`
|
||||
else:
|
||||
try:
|
||||
x, y = args
|
||||
except:
|
||||
raise Error, "bad coordinates: %s" % `args[0]`
|
||||
x0, y0 = self._origin
|
||||
self._goto(x0+x, y0-y)
|
||||
|
||||
def _goto(self, x1, y1):
|
||||
x0, y0 = start = self._position
|
||||
self._position = map(float, (x1, y1))
|
||||
if self._filling:
|
||||
self._path.append(self._position)
|
||||
if self._drawing:
|
||||
if self._tracing:
|
||||
dx = float(x1 - x0)
|
||||
dy = float(y1 - y0)
|
||||
distance = hypot(dx, dy)
|
||||
nhops = int(distance)
|
||||
item = self._canvas.create_line(x0, y0, x0, y0,
|
||||
width=self._width,
|
||||
arrow="last",
|
||||
capstyle="round",
|
||||
fill=self._color)
|
||||
try:
|
||||
for i in range(1, 1+nhops):
|
||||
x, y = x0 + dx*i/nhops, y0 + dy*i/nhops
|
||||
self._canvas.coords(item, x0, y0, x, y)
|
||||
self._canvas.update()
|
||||
self._canvas.after(10)
|
||||
self._canvas.itemconfigure(item, arrow="none")
|
||||
except Tk.TclError:
|
||||
# Probably the window was closed!
|
||||
return
|
||||
else:
|
||||
item = self._canvas.create_line(x0, y0, x1, y1,
|
||||
width=self._width,
|
||||
capstyle="round",
|
||||
fill=self._color)
|
||||
self._items.append(item)
|
||||
|
||||
|
||||
_root = None
|
||||
_canvas = None
|
||||
_pen = None
|
||||
|
||||
class Pen(RawPen):
|
||||
|
||||
def __init__(self):
|
||||
global _root, _canvas
|
||||
if _root is None:
|
||||
_root = Tk.Tk()
|
||||
_root.wm_protocol("WM_DELETE_WINDOW", self._destroy)
|
||||
if _canvas is None:
|
||||
# XXX Should have scroll bars
|
||||
_canvas = Tk.Canvas(_root, background="white")
|
||||
_canvas.pack(expand=1, fill="both")
|
||||
RawPen.__init__(self, _canvas)
|
||||
|
||||
def _destroy(self):
|
||||
global _root, _canvas, _pen
|
||||
root = self._canvas._root()
|
||||
if root is _root:
|
||||
_pen = None
|
||||
_root = None
|
||||
_canvas = None
|
||||
root.destroy()
|
||||
|
||||
|
||||
def _getpen():
|
||||
global _pen
|
||||
pen = _pen
|
||||
if not pen:
|
||||
_pen = pen = Pen()
|
||||
return pen
|
||||
|
||||
def degrees(): _getpen().degrees()
|
||||
def radians(): _getpen().radians()
|
||||
def reset(): _getpen().reset()
|
||||
def clear(): _getpen().clear()
|
||||
def tracer(flag): _getpen().tracer(flag)
|
||||
def forward(distance): _getpen().forward(distance)
|
||||
def backward(distance): _getpen().backward(distance)
|
||||
def left(angle): _getpen().left(angle)
|
||||
def right(angle): _getpen().right(angle)
|
||||
def up(): _getpen().up()
|
||||
def down(): _getpen().down()
|
||||
def width(width): _getpen().width(width)
|
||||
def color(*args): apply(_getpen().color, args)
|
||||
def write(arg, move=0): _getpen().write(arg, move)
|
||||
def fill(flag): _getpen().fill(flag)
|
||||
def circle(radius, extent=None): _getpen().circle(radius, extent)
|
||||
def goto(*args): apply(_getpen().goto, args)
|
||||
|
||||
def demo():
|
||||
reset()
|
||||
tracer(1)
|
||||
up()
|
||||
backward(100)
|
||||
down()
|
||||
# draw 3 squares; the last filled
|
||||
width(3)
|
||||
for i in range(3):
|
||||
if i == 2:
|
||||
fill(1)
|
||||
for j in range(4):
|
||||
forward(20)
|
||||
left(90)
|
||||
if i == 2:
|
||||
color("maroon")
|
||||
fill(0)
|
||||
up()
|
||||
forward(30)
|
||||
down()
|
||||
width(1)
|
||||
color("black")
|
||||
# move out of the way
|
||||
tracer(0)
|
||||
up()
|
||||
right(90)
|
||||
forward(100)
|
||||
right(90)
|
||||
forward(100)
|
||||
right(180)
|
||||
down()
|
||||
# some text
|
||||
write("startstart", 1)
|
||||
write("start", 1)
|
||||
color("red")
|
||||
# staircase
|
||||
for i in range(5):
|
||||
forward(20)
|
||||
left(90)
|
||||
forward(20)
|
||||
right(90)
|
||||
# filled staircase
|
||||
fill(1)
|
||||
for i in range(5):
|
||||
forward(20)
|
||||
left(90)
|
||||
forward(20)
|
||||
right(90)
|
||||
fill(0)
|
||||
# more text
|
||||
write("end")
|
||||
if __name__ == '__main__':
|
||||
_root.mainloop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
demo()
|
|
@ -0,0 +1,343 @@
|
|||
# LogoMation-like turtle graphics
|
||||
|
||||
from math import * # Also for export
|
||||
import Tkinter
|
||||
Tk = Tkinter
|
||||
Error = Exception
|
||||
|
||||
class RawPen:
|
||||
|
||||
def __init__(self, canvas):
|
||||
self._canvas = canvas
|
||||
self._items = []
|
||||
self._tracing = 1
|
||||
self.degrees()
|
||||
self.reset()
|
||||
|
||||
def degrees(self, fullcircle=360.0):
|
||||
self._fullcircle = fullcircle
|
||||
self._invradian = pi / (fullcircle * 0.5)
|
||||
|
||||
def radians(self):
|
||||
self.degrees(2.0*pi)
|
||||
|
||||
def reset(self):
|
||||
canvas = self._canvas
|
||||
width = canvas.winfo_width()
|
||||
height = canvas.winfo_height()
|
||||
if width <= 1:
|
||||
width = canvas['width']
|
||||
if height <= 1:
|
||||
height = canvas['height']
|
||||
self._origin = float(width)/2.0, float(height)/2.0
|
||||
self._position = self._origin
|
||||
self._angle = 0.0
|
||||
self._drawing = 1
|
||||
self._width = 1
|
||||
self._color = "black"
|
||||
self._filling = 0
|
||||
self._path = []
|
||||
self._tofill = []
|
||||
self.clear()
|
||||
canvas._root().tkraise()
|
||||
|
||||
def clear(self):
|
||||
self.fill(0)
|
||||
canvas = self._canvas
|
||||
items = self._items
|
||||
self._items = []
|
||||
for item in items:
|
||||
canvas.delete(item)
|
||||
|
||||
def tracer(self, flag):
|
||||
self._tracing = flag
|
||||
|
||||
def forward(self, distance):
|
||||
x0, y0 = start = self._position
|
||||
x1 = x0 + distance * cos(self._angle*self._invradian)
|
||||
y1 = y0 - distance * sin(self._angle*self._invradian)
|
||||
self._goto(x1, y1)
|
||||
|
||||
def backward(self, distance):
|
||||
self.forward(-distance)
|
||||
|
||||
def left(self, angle):
|
||||
self._angle = (self._angle + angle) % self._fullcircle
|
||||
|
||||
def right(self, angle):
|
||||
self.left(-angle)
|
||||
|
||||
def up(self):
|
||||
self._drawing = 0
|
||||
|
||||
def down(self):
|
||||
self._drawing = 1
|
||||
|
||||
def width(self, width):
|
||||
self._width = float(width)
|
||||
|
||||
def color(self, *args):
|
||||
if not args:
|
||||
raise Error, "no color arguments"
|
||||
if len(args) == 1:
|
||||
color = args[0]
|
||||
if type(color) == type(""):
|
||||
# Test the color first
|
||||
try:
|
||||
id = self._canvas.create_line(0, 0, 0, 0, fill=color)
|
||||
except Tk.TclError:
|
||||
raise Error, "bad color string: %s" % `color`
|
||||
self._color = color
|
||||
return
|
||||
try:
|
||||
r, g, b = color
|
||||
except:
|
||||
raise Error, "bad color sequence: %s" % `color`
|
||||
else:
|
||||
try:
|
||||
r, g, b = args
|
||||
except:
|
||||
raise Error, "bad color arguments: %s" % `args`
|
||||
assert 0 <= r <= 1
|
||||
assert 0 <= g <= 1
|
||||
assert 0 <= b <= 1
|
||||
x = 255.0
|
||||
y = 0.5
|
||||
self._color = "#%02x%02x%02x" % (int(r*x+y), int(g*x+y), int(b*x+y))
|
||||
|
||||
def write(self, arg, move=0):
|
||||
x, y = start = self._position
|
||||
x = x-1 # correction -- calibrated for Windows
|
||||
item = self._canvas.create_text(x, y,
|
||||
text=str(arg), anchor="sw",
|
||||
fill=self._color)
|
||||
self._items.append(item)
|
||||
if move:
|
||||
x0, y0, x1, y1 = self._canvas.bbox(item)
|
||||
self._goto(x1, y1)
|
||||
|
||||
def fill(self, flag):
|
||||
if self._filling:
|
||||
path = tuple(self._path)
|
||||
smooth = self._filling < 0
|
||||
if len(path) > 2:
|
||||
item = self._canvas._create('polygon', path,
|
||||
{'fill': self._color,
|
||||
'smooth': smooth})
|
||||
self._items.append(item)
|
||||
self._canvas.lower(item)
|
||||
if self._tofill:
|
||||
for item in self._tofill:
|
||||
self._canvas.itemconfigure(item, fill=self._color)
|
||||
self._items.append(item)
|
||||
self._path = []
|
||||
self._tofill = []
|
||||
self._filling = flag
|
||||
if flag:
|
||||
self._path.append(self._position)
|
||||
|
||||
def circle(self, radius, extent=None):
|
||||
if extent is None:
|
||||
extent = self._fullcircle
|
||||
x0, y0 = self._position
|
||||
xc = x0 - radius * sin(self._angle * self._invradian)
|
||||
yc = y0 - radius * cos(self._angle * self._invradian)
|
||||
if radius >= 0.0:
|
||||
start = self._angle - 90.0
|
||||
else:
|
||||
start = self._angle + 90.0
|
||||
extent = -extent
|
||||
if self._filling:
|
||||
if abs(extent) >= self._fullcircle:
|
||||
item = self._canvas.create_oval(xc-radius, yc-radius,
|
||||
xc+radius, yc+radius,
|
||||
width=self._width,
|
||||
outline="")
|
||||
self._tofill.append(item)
|
||||
item = self._canvas.create_arc(xc-radius, yc-radius,
|
||||
xc+radius, yc+radius,
|
||||
style="chord",
|
||||
start=start,
|
||||
extent=extent,
|
||||
width=self._width,
|
||||
outline="")
|
||||
self._tofill.append(item)
|
||||
if self._drawing:
|
||||
if abs(extent) >= self._fullcircle:
|
||||
item = self._canvas.create_oval(xc-radius, yc-radius,
|
||||
xc+radius, yc+radius,
|
||||
width=self._width,
|
||||
outline=self._color)
|
||||
self._items.append(item)
|
||||
item = self._canvas.create_arc(xc-radius, yc-radius,
|
||||
xc+radius, yc+radius,
|
||||
style="arc",
|
||||
start=start,
|
||||
extent=extent,
|
||||
width=self._width,
|
||||
outline=self._color)
|
||||
self._items.append(item)
|
||||
angle = start + extent
|
||||
x1 = xc + abs(radius) * cos(angle * self._invradian)
|
||||
y1 = yc - abs(radius) * sin(angle * self._invradian)
|
||||
self._angle = (self._angle + extent) % self._fullcircle
|
||||
self._position = x1, y1
|
||||
if self._filling:
|
||||
self._path.append(self._position)
|
||||
|
||||
def goto(self, *args):
|
||||
if len(args) == 1:
|
||||
try:
|
||||
x, y = args[0]
|
||||
except:
|
||||
raise Error, "bad point argument: %s" % `args[0]`
|
||||
else:
|
||||
try:
|
||||
x, y = args
|
||||
except:
|
||||
raise Error, "bad coordinates: %s" % `args[0]`
|
||||
x0, y0 = self._origin
|
||||
self._goto(x0+x, y0-y)
|
||||
|
||||
def _goto(self, x1, y1):
|
||||
x0, y0 = start = self._position
|
||||
self._position = map(float, (x1, y1))
|
||||
if self._filling:
|
||||
self._path.append(self._position)
|
||||
if self._drawing:
|
||||
if self._tracing:
|
||||
dx = float(x1 - x0)
|
||||
dy = float(y1 - y0)
|
||||
distance = hypot(dx, dy)
|
||||
nhops = int(distance)
|
||||
item = self._canvas.create_line(x0, y0, x0, y0,
|
||||
width=self._width,
|
||||
arrow="last",
|
||||
capstyle="round",
|
||||
fill=self._color)
|
||||
try:
|
||||
for i in range(1, 1+nhops):
|
||||
x, y = x0 + dx*i/nhops, y0 + dy*i/nhops
|
||||
self._canvas.coords(item, x0, y0, x, y)
|
||||
self._canvas.update()
|
||||
self._canvas.after(10)
|
||||
self._canvas.itemconfigure(item, arrow="none")
|
||||
except Tk.TclError:
|
||||
# Probably the window was closed!
|
||||
return
|
||||
else:
|
||||
item = self._canvas.create_line(x0, y0, x1, y1,
|
||||
width=self._width,
|
||||
capstyle="round",
|
||||
fill=self._color)
|
||||
self._items.append(item)
|
||||
|
||||
|
||||
_root = None
|
||||
_canvas = None
|
||||
_pen = None
|
||||
|
||||
class Pen(RawPen):
|
||||
|
||||
def __init__(self):
|
||||
global _root, _canvas
|
||||
if _root is None:
|
||||
_root = Tk.Tk()
|
||||
_root.wm_protocol("WM_DELETE_WINDOW", self._destroy)
|
||||
if _canvas is None:
|
||||
# XXX Should have scroll bars
|
||||
_canvas = Tk.Canvas(_root, background="white")
|
||||
_canvas.pack(expand=1, fill="both")
|
||||
RawPen.__init__(self, _canvas)
|
||||
|
||||
def _destroy(self):
|
||||
global _root, _canvas, _pen
|
||||
root = self._canvas._root()
|
||||
if root is _root:
|
||||
_pen = None
|
||||
_root = None
|
||||
_canvas = None
|
||||
root.destroy()
|
||||
|
||||
|
||||
def _getpen():
|
||||
global _pen
|
||||
pen = _pen
|
||||
if not pen:
|
||||
_pen = pen = Pen()
|
||||
return pen
|
||||
|
||||
def degrees(): _getpen().degrees()
|
||||
def radians(): _getpen().radians()
|
||||
def reset(): _getpen().reset()
|
||||
def clear(): _getpen().clear()
|
||||
def tracer(flag): _getpen().tracer(flag)
|
||||
def forward(distance): _getpen().forward(distance)
|
||||
def backward(distance): _getpen().backward(distance)
|
||||
def left(angle): _getpen().left(angle)
|
||||
def right(angle): _getpen().right(angle)
|
||||
def up(): _getpen().up()
|
||||
def down(): _getpen().down()
|
||||
def width(width): _getpen().width(width)
|
||||
def color(*args): apply(_getpen().color, args)
|
||||
def write(arg, move=0): _getpen().write(arg, move)
|
||||
def fill(flag): _getpen().fill(flag)
|
||||
def circle(radius, extent=None): _getpen().circle(radius, extent)
|
||||
def goto(*args): apply(_getpen().goto, args)
|
||||
|
||||
def demo():
|
||||
reset()
|
||||
tracer(1)
|
||||
up()
|
||||
backward(100)
|
||||
down()
|
||||
# draw 3 squares; the last filled
|
||||
width(3)
|
||||
for i in range(3):
|
||||
if i == 2:
|
||||
fill(1)
|
||||
for j in range(4):
|
||||
forward(20)
|
||||
left(90)
|
||||
if i == 2:
|
||||
color("maroon")
|
||||
fill(0)
|
||||
up()
|
||||
forward(30)
|
||||
down()
|
||||
width(1)
|
||||
color("black")
|
||||
# move out of the way
|
||||
tracer(0)
|
||||
up()
|
||||
right(90)
|
||||
forward(100)
|
||||
right(90)
|
||||
forward(100)
|
||||
right(180)
|
||||
down()
|
||||
# some text
|
||||
write("startstart", 1)
|
||||
write("start", 1)
|
||||
color("red")
|
||||
# staircase
|
||||
for i in range(5):
|
||||
forward(20)
|
||||
left(90)
|
||||
forward(20)
|
||||
right(90)
|
||||
# filled staircase
|
||||
fill(1)
|
||||
for i in range(5):
|
||||
forward(20)
|
||||
left(90)
|
||||
forward(20)
|
||||
right(90)
|
||||
fill(0)
|
||||
# more text
|
||||
write("end")
|
||||
if __name__ == '__main__':
|
||||
_root.mainloop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
demo()
|
Loading…
Reference in New Issue