Summary
Code which works outside of blender for any sized graph works in Blender for small-sized graphs but causes Blender to crash for moderately and up sized graphs.
detail
I'm trying to write an Addon which lets users manipulate Graph_tool (https://graph-tool.skewed.de/) Graphs in blender. I've been trying to achieve this using Icospheres as vertices and beveled NURMS curves as edges which have constraints on them to get them to stick to one vertex and stretch to another vertex. I've been attempting to save on memory by sharing mesh and curve Datablocks (I think thats the correct word although I am still a noob to blender python scripting).
I've got a basic class working which lets me do this. I've got graphs of around 300 nodes and 400 edges working. It takes about half a second to setup all the icospheres and curves and Blender reports it's using less than 50MB of memory once it's finished. However If I go much bigger than this Blender starts to crash during the execution of the script. I get errors such as terminate called after throwing an instance of 'terminate called recursively
or terminate called after throwing an instance of std::bad_allocWriting: /tmp/testing.crash.txt
. I've run the code outside of blender and everything seems to work fine (I set bpy to a dummy module), even for much much larger graphs (tens of thousands of nodes, hundreds of thousands of edges). I'm managed to find the line of my script which seems to be causing the issue when I run my script in blender. It works out a layout for the graph and can take quite a long time.
Does anyone have any idea what could be causing this or where to look? Is there any way to memory profile my script as it is run inside blender?
Here's my main class and how I use it:
import bpy
import graph_tool as gt
from graph_tool.draw import sfdp_layout
from graph_tool.generation import lattice
class Graph(gt.Graph):
def __init__(self,g=None,directed=False,prune=False,vorder=None):
super().__init__(g,directed,prune,vorder)
self.pos = None
self._vertices = Vertices()
self._edges = Edges()
self.n = self.num_vertices()
if g != None:
self.non_empty_setup()
def non_empty_setup(self):
#This is the line blender freaks out on for large graphs:
self.pos = sfdp_layout(self,C=5)
#It computes x,y position for each vertex in the graph
self._vertices.update(self,self.pos)
self._edges.update(self)
g_ = lattice([200,200]) # an inbuilt graph_tool function to generate a graph of a lattice
g = Graph(g_)
I can post the other helper classes (Vertices
and Edges
) If that would help.
For reproducibility, I've found the easiest way to install graph_tool for Blender is to sym-link the site-packages directory of Blender's internal python installation to the site-packages directory of a conda python installation of the exact same version and then install graph_tool using https://git.skewed.de/count0/graph-tool/-/wikis/installation-instructions#conda.
Please let me know if you think my strategy of using icospheres and curves is a dumb idea. I'm trying to find the most efficient approach which doesn't look ugly.
Edit
I've realized that my issue can be reproduced by running the following from the text editor:
g = lattice([20,20])
pos = sfdp_layout(g) # blender crashes here
This occurs even if bpy
is not imported. This led me to believe my issue is in fact with how I've set up my python interpreter. I've outlined how I set it up above. Is there a better way? Installing graph-tool is a pain. I've only worked out how to do it using conda.