Turtle graphics
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()