n1 = GraphNode('A', 0, 0, 200, 200) n2 = GraphNode('B', 0, 0, 200, 200) g.AddEdge(n1, n2) """ Spring force layout """ layouter = GraphLayoutSpring(g) def dump(): for node in g.nodes: print node, "layout info:", (node.layoutPosX, node.layoutPosY) print "layout pass 1" layouter.layout() dump() print "layout pass 2" layouter.layout() dump() """ Rendering Its only when you have a renderer that the idea of a world/screen coordinate system arises. """ class WorldCanvas: def __init__(self, width, height): self.width = width
def LayoutMultipleChooseBest(self, numlayouts=3): """ Blackboard Rerun layout several times, remembering each as a memento. Then pick the best. Don't remember starting scale since we are going to change it anyway We could add mementos for a layout at the current scale - in which case Coordinate scaling runs 3.2 to max within ScaleUpMadly() Finish at the best scale as chosen by this algorithm """ self.umlwin.AllToLayoutCoords() # doesn't matter what scale the layout starts with layouter = GraphLayoutSpring(self.graph, gui=self) self.umlwin.snapshot_mgr.Clear() oriscale = self.umlwin.coordmapper.scale def ThinkAndAddSnapshot(res): num_line_line_crossings, num_node_node_overlaps, num_line_node_crossings = res # Calculate a layout score, lower the better? Not used yet. score = 0 bounds = self.graph.GetBounds() self.umlwin.snapshot_mgr.AddSnapshot(\ layout_score=score, LL=num_line_line_crossings, NN=num_node_node_overlaps, LN=num_line_node_crossings, scale=self.umlwin.coordmapper.scale, bounds=bounds, bounds_area_simple=bounds[0]*bounds[1]/10000, graph_memento=self.graph.GetMementoOfPositions()) # Generate several totally fresh layout variations for i in range(numlayouts): progress_val = i+1 # range is 1..n inclusive whereas for loop is 0..n-1 excluding n, so adjust by adding 1 for visual progress if not self.outer_thread.CheckContinue(statusmsg="Layout #%d of %d" % (progress_val, numlayouts), progress=progress_val): break # Do a layout self.outer_thread.Log("spring layout started") layouter.layout(keep_current_positions=False) self.outer_thread.Log("layout done") if not self.outer_thread.CheckContinue(logmsg="GetVitalStats"): break # Expand directly to the original scale, and calc vitals stats res = self.GetVitalStats(scale=oriscale, animate=False) ThinkAndAddSnapshot(res) if res[0] == 0 and res[2] <= 0: # LL crossings solved and LN reasonable, so optimise and break - save time self.outer_thread.Log("LL crossings solved and LN reasonable, so optimise and break - save time") break if not self.outer_thread.CheckContinue(logmsg="ScaleUpMadly"): break # Expand progressively from small to large scale, and calc vitals stats # This can be SLOW res = self.ScaleUpMadly(strategy=":reduce post overlap removal LN crossings", animate=ANIMATE_BLACKBOARD_ATTEMPTS) ThinkAndAddSnapshot(res) if res[0] == 0 and res[2] <= 0: # LL crossings solved and LN reasonable, so optimise and break - save time break #self.umlwin.snapshot_mgr.DumpSnapshots(label='Unsorted') """ blackboard now sorting smarter because I have converted snapshots to dictionary format and thus can control which elements to sort by and whether to maximise or minimise any particular key in that snapshot dictionary. """ def sortfunc(d): # this does the thinking! return (d['LL'], d['LN'], d['bounds_area_simple'], -d['scale'], d['NN_pre_OR']) #self.umlwin.snapshot_mgr.Sort() self.umlwin.snapshot_mgr.Sort(sortfunc) # this does the thinking! #self.umlwin.snapshot_mgr.Sort(lambda d: (d['scale'], -d['LL'], -d['LN'])) # pick biggest with most line crossings! - Ha ha """Diagnostic""" #self.umlwin.snapshot_mgr.DumpSnapshots('Sorted') """ can't do the snapshot restore self.umlwin.snapshot_mgr.Restore(0) here since it will call stateofthenation(), and that is wx gui activity which is not allowed from inside a thread. So send a special message to trigger that call. """ self.outer_thread.Cmd("snapshot_mgr_restore_0")