Last active
May 2, 2022 01:30
-
-
Save itarato/dedf63317c4969956011d3d0abfb5853 to your computer and use it in GitHub Desktop.
Turtle-ish drawing with Python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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