From b241b67b8954b0679377af00d668e3dc92f4c858 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 4 Dec 1998 16:42:46 +0000 Subject: Turtle graphics --- Lib/lib-tk/turtle.py | 343 +++++++++++++++++++++++++++++++++++++++++++++++++++ Lib/turtle.py | 343 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 686 insertions(+) create mode 100644 Lib/lib-tk/turtle.py create mode 100644 Lib/turtle.py diff --git a/Lib/lib-tk/turtle.py b/Lib/lib-tk/turtle.py new file mode 100644 index 0000000..49375a7 --- /dev/null +++ b/Lib/lib-tk/turtle.py @@ -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() diff --git a/Lib/turtle.py b/Lib/turtle.py new file mode 100644 index 0000000..49375a7 --- /dev/null +++ b/Lib/turtle.py @@ -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() -- cgit v0.12