You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

498 lines
17 KiB

import contextlib, glfw, skia
import sys
import time
from datetime import datetime, timedelta, timezone
from pprint import pprint
from OpenGL import GL
from read_db import get_from_db, transfer_weight_from_db, transfer_author_weight_from_db, \
transfer_author_weight_from_db_days, transfer_source_analysis_from_db
import pickle
from treeplot import *
import magic
from colourpal import *
import math
import os
DATE_SPACE = 0
WIDTH, HEIGHT = 1500, 1000
RECT_HEIGHT = HEIGHT - DATE_SPACE
# https://gist.github.com/archeoid/6f4df73886730d09faabfe6d02e79fb2
DATA_CACHE = "treedata.b"
FILES_LIST_CACHE = "filesystemdata.b"
@contextlib.contextmanager
def glfw_window():
if not glfw.init():
raise RuntimeError("glfw.init() failed")
glfw.window_hint(glfw.STENCIL_BITS, 8)
window = glfw.create_window(WIDTH, HEIGHT, "", None, None) # glfw.get_primary_monitor()
glfw.make_context_current(window)
yield window
glfw.terminate()
@contextlib.contextmanager
def skia_surface(window):
context = skia.GrDirectContext.MakeGL()
(fb_width, fb_height) = glfw.get_framebuffer_size(window)
backend_render_target = skia.GrBackendRenderTarget(
fb_width,
fb_height,
0, # sampleCnt
0, # stencilBits
skia.GrGLFramebufferInfo(0, GL.GL_RGBA8),
)
surface = skia.Surface.MakeFromBackendRenderTarget(
context,
backend_render_target,
skia.kBottomLeft_GrSurfaceOrigin,
skia.kRGBA_8888_ColorType,
skia.ColorSpace.MakeSRGB(),
)
assert surface is not None
yield surface
context.abandonContext()
print("Loading")
from pathlib import Path
import os
if not os.path.exists(FILES_LIST_CACHE):
data = {}
# path = "/home/alistair/Documents/UQ/"
# path = "/home/alistair/Documents/UQ/2023/COSC3000"
path = "/run/media/alistair/storj/linux/"
for p in Path(path).glob("**/*"):
size = 1 # os.path.getsize(p)
if os.path.isfile(p) and "text" in magic.from_file(p):
try:
with open(p, "r") as f:
size = len(f.readlines())
except:
print("LDSALDjLKADJLAJDA")
elif os.path.isdir(p):
continue
pp = tuple(x for x in str(p).split("/") if len(x) > 0)
if len(pp) == 0:
continue
data[pp] = size
# for i in range(10000):
# if len({p[i+1:]:None}) == len(data):
# end = i
# break
i = 0
while len({k[i + 2:]: None for k in data}) == len(data):
i += 1
data = {k[i:]: v for k, v in data.items()}
clean = {}
for p, size in data.items():
p = p[1:]
if len(p) > 0:
clean[p] = DataFrame(p, size, size)
with open(FILES_LIST_CACHE, "wb") as f:
pickle.dump(clean, f)
else:
with open(FILES_LIST_CACHE, "rb") as f:
clean = pickle.load(f)
clearcolour = skia.ColorBLACK
padding = 0
leaves_only=True
tm = None
if not os.path.exists(DATA_CACHE):
# clean = get_from_db()
print("Building Tree")
map = TreeMap(clean)
print("MAP:", list(map.levels.values())[0:10])
tm = map.to_tree()
tm = tm.pack(Rect(0, 0, WIDTH, RECT_HEIGHT), padding=padding)
print("Setting labels")
for t in tm.items():
t.attribs["label"] = "/".join(t.path)
tm.attribs["label"] = "root"
print("Done.")
# transfer_weight_from_db(tm)
#dates = transfer_author_weight_from_db_days(tm)
transfer_author_weight_from_db(tm)
#tm.attribs["all_dates"] = dates
print("Saving to file", DATA_CACHE)
with open(DATA_CACHE, "wb") as f:
pickle.dump(tm, f)
else:
print("Loading from file", DATA_CACHE)
with open(DATA_CACHE, "rb") as f:
tm = pickle.load(f)
print("Packing tree")
tm: TreeMap = tm.pack(Rect(0, 0, WIDTH, RECT_HEIGHT), padding=padding)
print("Shrinking tree. Size:", tm.count_children())
# tm = tm.trim(threshold=0.05)
print("New size:", tm.count_children())
tm: TreeMap = tm.pack(Rect(0, 0, WIDTH, RECT_HEIGHT), padding=padding)
#transfer_source_analysis_from_db(tm)
rerooted = tm
parent = [rerooted]
child_frame = rerooted
anim_speed = 1.1
levels = 1000
#
# for node in tm.items():
# # With transfer_author_weight_from_db(tm)
# # Num deltas
# node.weight = 1.1 * math.log(1 + node.attribs["additions"] + node.attribs["deletions"])
#
def authors_heatmap():
global tm
max_authors = max(len(n.attribs["this_authors"]) for n in tm.items())
for node in tm.items():
node.weight = 5 * len(node.attribs["this_authors"]) / max_authors
# With transfer_author_weight_from_db(tm)
# num authors
set_colours_new(tm, hsv(0, 1, 1), hsv(364, 1, 1))
def additions_to_deletions_heatmap():
global tm
max_d = math.log(max(t.attribs["additions"] + t.attribs["deletions"] for t in tm.leaves()))
max_d = max(t.attribs["additions"] + t.attribs["deletions"] for t in tm.leaves())
print(max_d)
for node in tm.items():
# With transfer_author_weight_from_db(tm)
# Amount added
if (node.attribs["additions"] + node.attribs["deletions"]) > 0:
node.weight = math.log(node.attribs["additions"] + node.attribs["deletions"]) / math.log(max_d)
node.attribs["gradient"] = node.attribs["additions"] / (node.attribs["additions"] + node.attribs["deletions"])
else:
node.attribs["gradient"] = 0.5
node.weight = 0
grad = lerp_gradient(rgb(255, 90, 0 ), rgb(0, 90, 255), 100)
set_colours_gradient(tm, grad)
#
additions_to_deletions_heatmap()
#authors_heatmap()
#for node in tm.items():
# node.weight = 1.0
# Scaling for weight
# max_weight = max(node.weight for node in tm.items())
# scaling = 1/(max_weight)
# for node in tm.items():
# node.weight *= scaling
# print(max_weight)
print("Setting Colours")
#set_colours(tm, Color("red"), Color("blue"))
# set_colours_bw(tm)
#set_colours_new(tm, hsv(0, 1, 1.0), hsv(364, 1, 1.0))
tm.attribs["colour"] = rgb(255,255,255)
#for node in tm.items():
# # # With
# # # Amount added
# # # transfer_source_analysis_from_db(tm)
# node.weight = node.attribs["comment_lines"] / node.attribs["lines"] if node.attribs["lines"] > 0 else 0
# if not (node.attribs["is_source"] or node.attribs["is_header"]) and node.is_leaf():
# node.attribs["colour"] = hsv(0, 0, 0.2)
# node.weight = 1
## print(node.weight)
#
resize = False
finished_animation = True
start_animation = True
animation_is_going_up = False
dragging = False
translation = (0, 0)
zoom = 1
def handle_resize(window, width, height):
global resize
global rerooted
global WIDTH
global HEIGHT
WIDTH = width
HEIGHT = height
RECT_HEIGHT = height - DATE_SPACE
resize = True
rerooted = child_frame
rerooted = rerooted.pack(
Rect(0, 0, WIDTH, RECT_HEIGHT), padding=padding, rattrib="rect_target"
)
def handle_mouse_click(window, button, action, mods):
x, y = glfw.get_cursor_pos(window)
global rerooted
global update
global parent
global child_frame
global start_animation
global animating_up
global dragging
global translation
if action == glfw.PRESS and button == glfw.MOUSE_BUTTON_1:
if glfw.get_key(window, glfw.KEY_LEFT_SHIFT) == glfw.PRESS:
dragging = glfw.get_cursor_pos(window)
print("drag")
return
if action == glfw.RELEASE:
if dragging:
x, y = glfw.get_cursor_pos(window)
translation = (
translation[0] + x - dragging[0],
translation[1] + y - dragging[1],
)
dragging = None
return
if action == glfw.RELEASE and button == glfw.MOUSE_BUTTON_2:
rerooted = child_frame # if cancelling animation
new = parent.pop(-1)
new.pack(Rect(0, 0, WIDTH, RECT_HEIGHT), padding=padding, rattrib="rect_target")
child_frame = new
if (len(parent)) == 0:
parent = [new]
start_animation = True
animation_is_going_up = True
return
if not finished_animation:
return
if action == glfw.RELEASE and button == glfw.MOUSE_BUTTON_1:
new = rerooted.locate_at_position(x, y, levels=0)
if new is None:
return
parent.append(rerooted)
new.pack(Rect(0, 0, WIDTH, RECT_HEIGHT), padding=padding, rattrib="rect_target")
# rerooted = new
child_frame = new
start_animation = True
animation_is_going_up = False
def handle_scroll(window, x, y):
global zoom
zoom += 0.1 * y
zoom = max(min(zoom, 5), 1)
need_redraw = True
def main():
frame = 0
global resize
global levels
global rerooted
global finished_animation
global start_animation
animation_background = None
current_date = 0
with glfw_window() as window:
GL.glClear(GL.GL_COLOR_BUFFER_BIT)
glfw.set_window_size_callback(window, handle_resize)
glfw.set_mouse_button_callback(window, handle_mouse_click)
glfw.set_scroll_callback(window, handle_scroll)
while True:
with skia_surface(window) as surface:
def redraw(canvas, force=False):
global need_redraw
if not (force or need_redraw or resize or not finished_animation):
return
draw(
canvas,
rerooted,
max_level=levels,
label_level=2,
text_scale=2 / zoom,
leaves_only=leaves_only
)
canvas.scale(1 / zoom, 1 / zoom)
canvas.translate(-tx, -ty)
draw_labels(canvas, rerooted, max_level=levels, label_level=4, text_scale=1 / zoom)
draw_labels(canvas, rerooted, max_level=levels, label_level=3, text_scale=1 / zoom)
draw_labels(canvas, rerooted, max_level=levels, label_level=2, text_scale=2 / zoom)
draw_labels(canvas, rerooted, max_level=levels, label_level=1, text_scale=3 / zoom, autoscale=True)
need_redraw = False
while not resize:
if (
glfw.get_key(window, glfw.KEY_ESCAPE) == glfw.PRESS
) or glfw.window_should_close(window):
print("done")
return
if glfw.get_key(window, glfw.KEY_EQUAL) == glfw.PRESS:
levels += 1
if glfw.get_key(window, glfw.KEY_MINUS) == glfw.PRESS:
levels -= 1
if glfw.get_key(window, glfw.KEY_S) == glfw.PRESS:
outf = "out/" + str(time.time()) + "-" "_".join(rerooted.path)
print("Saving")
image = surface.makeImageSnapshot()
if image is not None:
image.save(outf + ".png", skia.kPNG)
else:
print("Error saving")
# https://kyamagu.github.io/skia-python/tutorial/canvas.html#pdf
stream = skia.FILEWStream(outf + ".pdf")
with skia.PDF.MakeDocument(stream) as document:
with document.page(WIDTH, HEIGHT) as canvas:
redraw(canvas, force=True)
stream.flush()
print("Saved PDF")
# Next date
# if "all_dates" in tm.attribs:
# this_date = tm.attribs["all_dates"][current_date]
# print("date: ", this_date)
# for k in tm.items():
# if this_date in k.attribs["additions_date"]:
# k.weight = math.log(1 + k.attribs["additions_date"][this_date] + k.attribs["deletions_date"][this_date])
# k.weight *= 0.1
# else:
# k.weight = 0
# current_date = (current_date + 1) % len(tm.attribs["all_dates"])
# #set_colours_bw(tm)
# #set_colours(tm, Color("red"), Color("blue"))
# tm.attribs["colour"] = Color("white")
tx, ty = translation
if dragging:
tx, ty = (
translation[0] + x - dragging[0],
translation[1] + y - dragging[1],
)
# Mouse transformed to canvas
x, y = glfw.get_cursor_pos(window)
adj_x, adj_y = (
(x + translation[0]) / zoom,
(y + translation[1]) / zoom,
)
# highlight
#highlighted = rerooted.locate_at_position(adj_x, adj_y, levels=0)
#highlighted.attribs["highlight"] = False
# Interpolate animation
if start_animation:
start_animation = False
if not animation_is_going_up:
animation_background = surface.makeImageSnapshot()
finished_animation = True
for t in child_frame.items():
if "rect_target" in t.attribs:
t.attribs["rect"], finished = t.attribs[
"rect"
].step_towards(t.attribs["rect_target"], amount=anim_speed)
if finished:
t.attribs.pop("rect_target")
else:
finished_animation = False
if not finished_animation:
with surface as canvas:
canvas.translate(tx, ty)
canvas.scale(zoom, zoom)
canvas.clear(clearcolour)
canvas.drawImage(animation_background, 0, 0)
draw(
canvas,
child_frame,
max_level=levels,
label_level=-1,
leaves_only=leaves_only
)
canvas.scale(1 / zoom, 1 / zoom)
canvas.translate(-tx, -ty)
else:
rerooted = child_frame
with surface as canvas:
redraw(canvas, force=True)
# ddd = datetime.strptime(time.ctime(this_date * 60 * 60 * 24), "%a %b %d %H:%M:%S %Y")
# draw_text(canvas, ddd.strftime("Week from %d %b %Y"), 10, 1080 - 10, size=30)
# Draw label tooltip
treeframe = rerooted.locate_at_position(
adj_x, adj_y, levels=levels - 1
)
if glfw.get_key(window, glfw.KEY_SPACE) == glfw.PRESS:
pprint(treeframe)
pprint(treeframe.attribs)
treeframe = None
if treeframe is not None:
text = skia.TextBlob(
treeframe.attribs["label"],
# + " value: "
# + str(treeframe.size),
skia.Font(None, 12),
)
textb = text.bounds()
tw, th = textb.width(), textb.height()
paint1 = skia.Paint(AntiAlias=True, Color=skia.ColorWHITE)
paint2 = skia.Paint(AntiAlias=True, Color=skia.ColorBLACK)
rx = min(x, WIDTH - tw - 10)
ry = y + th + 15
bg = skia.Rect(rx, ry, rx + tw, ry + th)
with surface as canvas:
# canvas.drawRect(bg, paint1)
# canvas.scale(zoom, zoom)
# canvas.translate(tx, ty)
canvas.drawTextBlob(text, rx, ry, paint2)
canvas.drawTextBlob(text, rx, ry, paint2)
# canvas.translate(-tx, -ty)
# canvas.scale(1 / zoom, 1 / zoom)
surface.flushAndSubmit()
glfw.swap_buffers(window)
#glfw.poll_events()
glfw.wait_events()
#highlighted.attribs.pop("highlight")
# image = surface.makeImageSnapshot()
# image.save(f"anim/{current_date:04}-out.png", skia.kPNG)
resize = False
# import cProfile
# cProfile.run('main()')
main()
sys.exit(0)