Last active
December 19, 2024 16:46
-
-
Save tbnorth/878161e61f0cc3457d8292719428c41c to your computer and use it in GitHub Desktop.
drawsvg experiment, draw a fish timeline chart
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
"""Draw a simple SVG timeline chart with Python.""" | |
import sys | |
import drawsvg as draw | |
w = 1200 | |
h = 800 | |
ox = -100 | |
oy = -400 | |
def create_drawing() -> draw.Drawing: | |
"""Create a drawing with a timeline.""" | |
dw = draw.Drawing(w, h, origin=(ox, oy)) | |
# white background | |
dw.append(draw.Rectangle(ox, oy, w, h, fill="#f0f0f0")) | |
# |--+--+--+--| timeline | |
# Q1 Q2 Q3 Q4 | |
dw.append(draw.Lines(0, 0, 1000, 0, stroke="black", stroke_width=3)) | |
for line in range(0, 1001, 250): | |
dw.append(draw.Lines(line, 10, line, -10, stroke="black", stroke_width=3)) | |
dw.append(draw.Text("Q1", 25, x=125, y=25, text_anchor="middle")) | |
dw.append(draw.Text("Q2", 25, x=375, y=25, text_anchor="middle")) | |
dw.append(draw.Text("Q3", 25, x=595, y=25, text_anchor="middle")) | |
dw.append(draw.Text("Q4", 25, x=875, y=25, text_anchor="middle")) | |
return dw | |
def draw_task( | |
dw: draw.Drawing, # drawing context | |
text: str, # text to lable task | |
done: float, # fraction done, 0-1 | |
pos: str, # position in year, 0-1000 | |
dir: str = "up", # up: below line sloping up, "down", above sloping down | |
) -> None: | |
"""Draw a task on the timeline.""" | |
slope = 0.5 | |
x0 = pos - 300 * slope | |
y0 = 300 | |
x1 = pos | |
y1 = 0 | |
off = -1 | |
if dir != "up": | |
y0 *= -1 | |
off *= -1 | |
p = draw.Lines( # done part of line, blue | |
x0, | |
y0, | |
(x1 - x0) * done + x0, | |
(y1 - y0) * done + y0, | |
stroke="blue", | |
stroke_width=3, | |
) | |
dw.append(p) | |
p = draw.Lines( # remaining part of line, red | |
(x1 - x0) * done + x0, | |
(y1 - y0) * done + y0, | |
x1, | |
y1, | |
stroke="red", | |
stroke_width=3, | |
) | |
dw.append(p) | |
# Couldn't find a way to offset text from line, so draw a second, invisible | |
# line to put the text on. | |
offs = 33 # offset for two lines of text | |
p = draw.Lines(x0 + offs * off, y0, x1 + offs * off, y1, stroke_width=0) | |
dw.append(p) | |
dw.append(draw.Text(text.split(r"\n"), 22, path=p)) | |
def main() -> None: | |
"""Generate chart from inputs.""" | |
dw = create_drawing() | |
with open(sys.argv[1]) as data: | |
title = data.readline() | |
tasks = list(i.split(None, 3) for i in data if i.strip()) | |
dw.append(draw.Text(title, 32, x=500, y=-360, text_anchor="middle")) | |
for done_txt, pos_txt, dir, text in tasks: | |
done = float(done_txt) | |
pos = float(pos_txt) | |
draw_task(dw, text, done, pos, dir) | |
dw.save_svg(f"{sys.argv[2]}.svg") | |
dw.save_png(f"{sys.argv[2]}.png") | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment