Skip to content

Instantly share code, notes, and snippets.

@itarato
Last active May 2, 2022 01:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save itarato/dedf63317c4969956011d3d0abfb5853 to your computer and use it in GitHub Desktop.
Save itarato/dedf63317c4969956011d3d0abfb5853 to your computer and use it in GitHub Desktop.
Turtle-ish drawing with Python
import math
from gi.repository import Gtk
import cairo
L90 = 90.0
R90 = -90.0
class Position():
def __init__(self):
self.x = 0.0
self.y = 0.0
self.angle = 0.0
def forward(self, value):
self.y += math.cos(self.angle) * value
self.x += math.sin(self.angle) * value
def coord(self):
return (self.x, self.y)
def rotate_rad(self, value):
self.angle += value
def move_to(self, x, y):
self.x = x
self.y = y
class Turtle():
def __init__(self, drawer):
self.is_pen_down = True
self.pen_width = 2.0
self.pos = Position()
self.drawer = drawer
def move_to(self, x, y):
self.pos.move_to(x, y)
def forward(self, value):
coords_old = self.pos.coord()
self.pos.forward(value)
coords_new = self.pos.coord()
if self.is_pen_down:
self.drawer.draw_line(coords_old, coords_new, width=self.pen_width)
def rotate_rad(self, value):
self.pos.rotate_rad(value)
def rotate_deg(self, value):
self.pos.rotate_rad((math.pi / 180.0) * value)
def pen_down(self):
self.is_pen_down = True
def pen_up(self):
self.is_pen_down = False
def set_width(self, value):
self.pen_width = value
def coord(self):
return self.pos.coord()
def line(self, lhs, rhs):
self.drawer.draw_line(lhs, rhs, width=self.pen_width)
class DebugDrawer():
def draw_line(self, lhs, rhs, width=0.0):
print(f"{lhs} -> {rhs} ({width})")
class Line():
def __init__(self, lhs_coords, rhs_coords, width=2.0) -> None:
self.lhs_coords = lhs_coords
self.rhs_coords = rhs_coords
self.color = (0.0, 0.0, 0.0)
self.width = width
class GtkDrawer(Gtk.Window):
def __init__(self):
super(GtkDrawer, self).__init__()
self.tran_setup()
self.init_ui()
self.lines = []
def init_ui(self):
self.connect("draw", self.on_draw)
self.set_title("Turtle")
self.resize(1024, 1024)
self.set_position(Gtk.WindowPosition.CENTER)
self.connect("delete-event", Gtk.main_quit)
self.show_all()
def tran_setup(self):
self.set_app_paintable(True)
screen = self.get_screen()
visual = screen.get_rgba_visual()
if visual != None and screen.is_composited():
self.set_visual(visual)
def on_draw(self, _, cr):
cr.set_source_rgba(1.0, 1.0, 1.0)
cr.paint()
cr.set_source_rgba(0.2, 0.2, 0.2, 0.4)
cr.set_operator(cairo.OPERATOR_SOURCE)
for line in self.lines:
cr.set_source_rgb(*line.color)
cr.set_line_width(line.width)
cr.move_to(*line.lhs_coords)
cr.line_to(*line.rhs_coords)
cr.stroke()
def draw_line(self, lhs, rhs, width=2.0):
self.lines.append(Line(lhs, rhs, width=width))
def draw_tree(t, len, limit, angle=30, pen_width=4.0):
if len <= limit:
return
t.set_width(pen_width)
t.forward(len)
t.rotate_deg(-angle)
draw_tree(t, len * 0.8, limit, angle=angle*1.0, pen_width=pen_width*0.7)
t.rotate_deg(angle * 2.0)
draw_tree(t, len * 0.8, limit, angle=angle*1.0, pen_width=pen_width*0.7)
t.rotate_deg(-angle)
t.forward(-len)
def draw_space_filling(t, len, limit):
if len <= limit:
t.pen_up()
t.forward(-len / 2.0)
t.rotate_deg(L90)
t.forward(len / 2.0)
t.rotate_deg(R90)
t.pen_down()
coord_start = t.coord()
t.forward(len)
t.rotate_deg(R90)
t.forward(len)
t.rotate_deg(R90)
t.forward(len)
coord_end = t.coord()
t.pen_up()
t.forward(-len / 2.0)
t.rotate_deg(R90)
t.forward(len / 2.0)
t.rotate_deg(R90)
return (coord_start, coord_end)
else:
# Move it to quadrant bottom-left
t.pen_up()
t.forward(-len / 2.0)
t.rotate_deg(L90)
t.forward(len / 2.0)
t.rotate_deg(R90)
t.rotate_deg(R90)
(start1, end1) = draw_space_filling(t, len / 2.0, limit)
t.rotate_deg(L90)
t.pen_up()
t.forward(len)
(start2, end2) = draw_space_filling(t, len / 2.0, limit)
t.rotate_deg(R90)
t.pen_up()
t.forward(len)
t.rotate_deg(L90)
(start3, end3) = draw_space_filling(t, len / 2.0, limit)
t.rotate_deg(180.0)
t.pen_up()
t.forward(len)
t.rotate_deg(R90)
(start4, end4) = draw_space_filling(t, len / 2.0, limit)
t.rotate_deg(L90)
# Move it back to center
t.pen_up()
t.forward(-len / 2.0)
t.rotate_deg(R90)
t.forward(len / 2.0)
t.rotate_deg(R90)
t.line(start1, start2)
t.line(end2, start3)
t.line(end3, end4)
return (end1, start4)
if __name__ == '__main__':
t = Turtle(GtkDrawer())
t.rotate_deg(180)
t.move_to(512, 512)
# t.forward(120.0)
# t.rotate_deg(90.0)
# t.forward(60.0)
# draw_tree(t, 140, 10, angle=20.0, pen_width=32.0)
t.set_width(1.0)
draw_space_filling(t, 512.0, 8.0)
Gtk.main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment