Skip to content

Instantly share code, notes, and snippets.

@villares
Last active December 13, 2020 01:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save villares/112616fb772e0377d112ddb4c4c93f34 to your computer and use it in GitHub Desktop.
Save villares/112616fb772e0377d112ddb4c4c93f34 to your computer and use it in GitHub Desktop.
"""
More naive graph drawing
Using Processing Python mode
To run this you'll need:
https://abav.lugaralgum.com/como-instalar-o-processing-modo-python/index-EN.html
"""
nodes = []
edges = []
NODE_SIZE = 50 # Diâmetro dos nodes
EDGE_SIZE = 100 # Distância sugerida para Edges
BACKGROUND = color(10, 0, 0)
def setup():
size(400, 400)
strokeWeight(3)
# graph = read_csv("graph.txt")
# Dictionary (hash map) of nodes and a list of connections (to make edges)
graph = {'A': ['B'], 'B': ['C'], 'C': ['A', 'B'],
'D': ['A'], 'E': ['A'], 'F': ['B'], 'G': ['F'],
# 'Z': ['W'], 'W': ['X', 'Y'], 'Y': ['W'], 'X': []
}
# create Node objects for each dictionary item
for name in graph:
x = random(width * .25, width * .75)
y = random(height * .25, height * .75)
new_node = Node(x, y, name)
nodes.append(new_node)
for name in graph:
n1 = Node.from_name(name) # find the Node object from its "name"
for other in graph[name]:
n2 = Node.from_name(other) # for each connection, find Node obj
# n2 not none and n1, n2 Edge not yet made
if n2 and not Edge.find(n1, n2):
new_edge = Edge(n1, n2) # create an Edge object
edges.append(new_edge)
def draw():
background(BACKGROUND) # limpa a tela
# para cada Edge
for e in edges:
e.plot() # desenha a linha
e.ajust(EDGE_SIZE)
# para cada n
for n in nodes:
n.plot() # desenha
n.move() # atualiza posição
# Sob clique do mouse seleciona/deseleciona nodes ou Edgesho
def mouseClicked():
for n in nodes: # para cada n checa distância do mouse
if dist(mouseX, mouseY, n.x, n.y) < NODE_SIZE / 2:
n.sel = not n.sel # inverte status de seleção
def keyPressed(): # Quando uma tecla é pressionada
print("key")
if key == 'r': # Se a tecla 'r' for pressionada
print("r")
for n in nodes:
print("for")
# sorteia nova posição
x = random(width * .25, width * .75)
y = random(height * .25, height * .75)
n.x, n.y = x, y
def mouseDragged(): # quando o mouse é arrastado
for n in nodes: # para cada n checa distância do mouse
if dist(mouseX, mouseY, n.x, n.y) < NODE_SIZE / 2:
n.x, n.y = mouseX, mouseY
n.v = PVector(0, 0)
def read_csv(file_name):
import csv
# ATENTION! Processing csv reader roots to sketch/data/ folder
# Pyhton csv module roots to the sketch folder!
with open(file_name) as f:
graph = {}
for record in csv.reader(f):
cols = [uniname(item.strip(), 'utf-8') for item in record]
graph[cols[0]] = list(cols[1:])
return graph
class Node(PVector):
"""
Node object inherits from PVector so we can do subtraction and
find direction between two nodes.
"""
V_MAX = 2 # velocnameade máxima nas ortogonais vx e vy
V_MIN = .6
def __init__(self, x, y, name):
self.name = name
self.x = x
self.y = y
self.v = PVector(0, 0)
self.sel = False # se está selecionado, começa sem seleção
self.cor = color(random(128, 255), # R
random(128, 255), # G
random(128, 255), # B
255) # Alpha ~50%
def plot(self):
stroke(self.cor)
fill(BACKGROUND)
ellipse(self.x, self.y, NODE_SIZE, NODE_SIZE)
texto = self.name
if dist(mouseX, mouseY, self.x, self.y) < NODE_SIZE / 2:
stroke(255)
noFill()
ellipse(self.x, self.y, NODE_SIZE - 5, NODE_SIZE - 5)
elif self.sel:
stroke(0)
noFill()
ellipse(self.x, self.y, NODE_SIZE - 5, NODE_SIZE - 5)
fill(self.cor)
textAlign(CENTER, CENTER)
text(texto, self.x, self.y)
def move(self):
if self.x < NODE_SIZE:
self.x = NODE_SIZE
elif self.x > width - NODE_SIZE:
self.x = width - NODE_SIZE
if self.y < NODE_SIZE:
self.y = NODE_SIZE
elif self.y > height - NODE_SIZE:
self.y = height - NODE_SIZE
for n in nodes:
if n is not self:
self.ajust(n)
self.limit_v()
if not self.sel:
self.x += self.v.x
self.y += self.v.y
def ajust(self, other):
dir = PVector.sub(self, other)
ms = dir.magSq() / 20 + EPSILON
# dir.normalize()
self.v += dir.div(ms)
def limit_v(self):
""" limita à velocidade máxima e para se estiver devagar"""
if self.v.mag() < Node.V_MIN:
self.v = PVector(0, 0)
else:
self.v.limit(Node.V_MAX)
@staticmethod
def from_name(name):
for p in nodes:
if p.name == name:
return p
return None
class Edge():
""" Edges have two nodes"""
def __init__(self, n1, n2):
self.n1 = n1
self.n2 = n2
self.nodes = set((n1, n2))
def plot(self):
stroke(255)
line(self.n1.x, self.n1.y, self.n2.x, self.n2.y)
def ajust(self, edge_size):
n1, n2 = self.n1, self.n2
d = dist(n1.x, n1.y, n2.x, n2.y)
delta = edge_size - d
dir = PVector.sub(n1, n2)
dir.mult(delta / 2000)
n1.v += dir
n2.v -= dir
@staticmethod
def find(n1, n2):
""" Is there an Edge with theses Nodes? """
for e in edges:
if n1 in e.nodes and n2 in e.nodes:
return True
return False
"""
More naive graph drawing
A directed graph from a list of edges
"""
nodes = []
edges = []
NODE_SIZE = 50 # Diâmetro dos nodes
EDGE_SIZE = 100 # Distância sugerida para Edges
BACKGROUND = color(10, 0, 0)
def setup():
size(400, 400)
strokeWeight(3)
graph = [[6, 2], [10, 11], [3, 6], [9, 2], # [2, 9],
[11, 9], [3, 8], [5, 11], [1, 4], [7, 3]]
proposed_nodes = set()
for na, nb in graph:
proposed_nodes.add(na)
proposed_nodes.add(nb)
for name in proposed_nodes:
x = random(width * .25, width * .75)
y = random(height * .25, height * .75)
new_node = Node(x, y, name)
nodes.append(new_node)
for na, nb in graph:
n1 = Node.from_name(na) # find the Node object from its "name"
n2 = Node.from_name(nb) # for each connection, find Node obj
new_edge = Edge(n1, n2) # create an Edge object
edges.append(new_edge)
def draw():
background(BACKGROUND) # limpa a tela
# para cada Edge
for e in edges:
e.plot() # desenha a linha
e.ajust(EDGE_SIZE)
# para cada n
for n in nodes:
n.plot() # desenha
n.move() # atualiza posição
# Sob clique do mouse seleciona/deseleciona nodes ou Edgesho
def mouseClicked():
for n in nodes: # para cada n checa distância do mouse
if dist(mouseX, mouseY, n.x, n.y) < NODE_SIZE / 2:
n.sel = not n.sel # inverte status de seleção
def keyPressed(): # Quando uma tecla é pressionada
if key == 'r': # Se a tecla 'r' for pressionada
for n in nodes:
# sorteia nova posição
x = random(width * .25, width * .75)
y = random(height * .25, height * .75)
n.x, n.y = x, y
def mouseDragged(): # quando o mouse é arrastado
for n in nodes: # para cada n checa distância do mouse
if dist(mouseX, mouseY, n.x, n.y) < NODE_SIZE / 2:
n.x, n.y = mouseX, mouseY
n.v = PVector(0, 0)
class Node(PVector):
"""
Node object inherits from PVector so we can do subtraction and
find direction between two nodes.
"""
V_MAX = 2 # velocnameade máxima nas ortogonais vx e vy
V_MIN = .6
def __init__(self, x, y, name):
self.name = name
self.x = x
self.y = y
self.v = PVector(0, 0)
self.sel = False # se está selecionado, começa sem seleção
self.cor = color(random(128, 255), # R
random(128, 255), # G
random(128, 255), # B
255) # Alpha ~50%
def plot(self):
stroke(self.cor)
fill(BACKGROUND)
ellipse(self.x, self.y, NODE_SIZE, NODE_SIZE)
texto = self.name
if dist(mouseX, mouseY, self.x, self.y) < NODE_SIZE / 2:
stroke(255)
noFill()
ellipse(self.x, self.y, NODE_SIZE - 5, NODE_SIZE - 5)
elif self.sel:
stroke(0)
noFill()
ellipse(self.x, self.y, NODE_SIZE - 5, NODE_SIZE - 5)
fill(self.cor)
textAlign(CENTER, CENTER)
text(texto, self.x, self.y)
def move(self):
if self.x < NODE_SIZE:
self.x = NODE_SIZE
elif self.x > width - NODE_SIZE:
self.x = width - NODE_SIZE
if self.y < NODE_SIZE:
self.y = NODE_SIZE
elif self.y > height - NODE_SIZE:
self.y = height - NODE_SIZE
for n in nodes:
if n is not self:
self.ajust(n)
self.limit_v()
if not self.sel:
self.x += self.v.x
self.y += self.v.y
def ajust(self, other):
dir = PVector.sub(self, other)
ms = dir.magSq() / 20 + EPSILON
# dir.normalize()
self.v += dir.div(ms)
def limit_v(self):
""" limita à velocidade máxima e para se estiver devagar"""
if self.v.mag() < Node.V_MIN:
self.v = PVector(0, 0)
else:
self.v.limit(Node.V_MAX)
@staticmethod
def from_name(name):
for p in nodes:
if p.name == name:
return p
return None
class Edge():
""" Edges have two nodes"""
def __init__(self, n1, n2):
self.n1 = n1
self.n2 = n2
self.nodes = [n1, n2] # directed now...
def plot(self):
stroke(255)
arrow(self.n1.x, self.n1.y,
self.n2.x, self.n2.y,
shorter=NODE_SIZE / 2,
head=10)
def ajust(self, edge_size):
n1, n2 = self.n1, self.n2
d = dist(n1.x, n1.y, n2.x, n2.y)
delta = edge_size - d
dir = PVector.sub(n1, n2)
dir.mult(delta / 2000)
n1.v += dir
n2.v -= dir
@staticmethod
def find(n1, n2):
""" Is there an Edge with theses Nodes? """
for e in edges:
if n1 == e.nodes[0] and n2 == e.nodes[1]: # directed now...
return True
return False
def arrow(xa, ya, xb, yb, shorter=0, head=None):
L = dist(xa, ya, xb, yb)
siz = L - shorter
siz_ponta = head or siz / 4 * sqrt(2)
ang = atan2(yb - ya, xb - xa)
xp = xa + cos(ang) * siz
yp = ya + sin(ang) * siz
line(xa, ya, xp, yp) # corpo com sizanho fixo
xpe = xp + cos(ang + QUARTER_PI + PI) * siz_ponta
ype = yp + sin(ang + QUARTER_PI + PI) * siz_ponta
line(xp, yp, xpe, ype) # parte esquerda da ponta
xpd = xp + cos(ang - QUARTER_PI + PI) * siz_ponta
ypd = yp + sin(ang - QUARTER_PI + PI) * siz_ponta
line(xp, yp, xpd, ypd) # parte direita da ponta
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment