import java.util.*; import java.applet.*; public class Blackboard { protected Vector _edges; protected Vector _nodes; protected Stack _history; protected GraphApplet _applet; public Globals globals; public Matrix3D projection; public Blackboard( GraphApplet applet ) { _edges = new Vector(); _nodes = new Vector(); _history = new Stack(); _applet = (GraphApplet) applet; globals = new Globals(this); projection = new Matrix3D(); _embedders = new Hashtable(); } public final GraphApplet applet() { return _applet; } // Node and Edge stuff // public final Vector edges() { return _edges; } public final Vector nodes() { return _nodes; } public Node addNode(String lbl) { Node n = new Node( this, lbl ); _nodes.addElement(n); return n; } public Node findNode(String lbl) { int nodecnt = _nodes.size(); for (int i = 0; i < nodecnt; ++i ) { Node n = (Node) _nodes.elementAt(i); if (n.label().equals(lbl)) return n; } return addNode(lbl); } public Edge addEdge(String f, String t) { Node from = findNode(f); Node to = findNode(t); Edge e = new Edge( this, from, to ); _edges.addElement( e ); from.add_out( e ); to.add_in( e ); return e; } public double Norm(Node u, Node v) { double dx = v.x() - u.x(); double dy = v.y() - u.y(); double dz = v.z() - u.z(); return Math.sqrt( dx*dx + dy*dy + dz*dz ); } // Assign layers, break cycles. // protected final void unmarkNodes() { int nodecnt = _nodes.size(); for (int i = 0; i < nodecnt; ++i ) { Node n = (Node) _nodes.elementAt(i); n.unmark(); } } protected final void break_cycles( Node curr ) { curr.mark(); curr.pick( true ); Vector outedges = curr.outedges(); int outedgecnt = outedges.size(); for (int i = 0; i < outedgecnt; ++i ) { Edge e = (Edge) outedges.elementAt(i); Node n = e.to(); if( n.picked() ) e.reverseDir(); else if( !n.marked() ) break_cycles( n ); } curr.pick( false ); } protected final void fixReversedEdges() { int edgecnt = _edges.size(); for (int i = 0; i < edgecnt; ++i ) { Edge e = (Edge) _edges.elementAt(i); if( e.reversed() ) { Node n = e.to(); n.remove_in( e ); n.add_out( e ); n = e.from(); n.remove_out( e ); n.add_in( e ); e.swapDir(); } } } public final void topoSort( Node curr, Vector topoSortedNodes ) { curr.mark(); Vector inedges = curr.inedges(); int inedgecnt = inedges.size(); for (int i = 0; i < inedgecnt; ++i ) { Node n = ((Edge) inedges.elementAt(i)).from(); if( n.marked() == false ) topoSort( n, topoSortedNodes ); } topoSortedNodes.addElement( curr ); } // Create and remove a meta-root and meta-edges to all nodes. // protected Node _meta_root; protected Vector _meta_edges; protected Node makeMetaRoot() { _meta_root = new Node( this, "meta-root" ); _meta_edges = new Vector(); int nodecnt = _nodes.size(); for (int i = 0; i < nodecnt; ++i ) { Node n = (Node) _nodes.elementAt(i); Edge e = new Edge( this, n, _meta_root ); _meta_edges.addElement( e ); _edges.addElement( e ); n.add_out( e ); _meta_root.add_in( e ); } _nodes.addElement( _meta_root ); return _meta_root; } protected void removeMetaRoot() { int junkcnt = _meta_edges.size(); for (int i = 0; i < junkcnt; ++i ) { Edge e = (Edge) _meta_edges.elementAt(i); _edges.removeElement( e ); e.from().remove_out( e ); e.to().remove_in( e ); } _nodes.removeElement( _meta_root ); } public void PreprocessNodes() { // Break any cycles in the graph by reversing some edges unmarkNodes(); int nodecnt = _nodes.size(); for (int i = 0; i < nodecnt; ++i ) { Node n = (Node) _nodes.elementAt(i); if( !n.marked() ) break_cycles( n ); } fixReversedEdges(); // Do a topological sort on the meta-graph, then assign levels to nodes. Vector topoSortedNodes = new Vector(); Node meta_root = makeMetaRoot(); unmarkNodes(); topoSort( meta_root, topoSortedNodes ); removeMetaRoot(); int toposize = topoSortedNodes.size(); int maxlevel = 0; for (int i = 0; i < toposize; ++i ) { Node v = (Node) topoSortedNodes.elementAt(i); int level = 0; Vector inedges = v.inedges(); int inedgecnt = inedges.size(); for (int j = 0; j < inedgecnt; ++j ) { Node u = ((Edge) inedges.elementAt(j)).from(); if( u.level() > level ) level = u.level(); } v.level( level+1 ); if( level+1 > maxlevel ) maxlevel = level+1; } // Hoist the nodes up to the maximum possible usage level. for (int i = toposize-1; i >= 0; --i ) { Node v = (Node) topoSortedNodes.elementAt(i); int min_useage_level = maxlevel; Vector outedges = v.outedges(); int outedgecnt = outedges.size(); if( outedgecnt == 0 ) min_useage_level = v.level(); for (int j = 0; j < outedgecnt; ++j ) { Node u = ((Edge) outedges.elementAt(j)).to(); int useage_level = u.useage_level()-1; if( useage_level < min_useage_level ) min_useage_level = useage_level; } v.useage_level( min_useage_level ); } for (int i = 0; i < toposize; ++i ) { Node v = (Node) topoSortedNodes.elementAt(i); v.level( v.useage_level() ); } } // Add and remove dummy nodes between nodes between levels that are far apart protected boolean _hasDummies = false; public boolean hasDummies() { return _hasDummies; } public synchronized void addDummies() { if( _hasDummies ) return; _hasDummies = true; int nodecnt = _nodes.size(); for (int i = 0; i < nodecnt; ++i ) { Node to = (Node) _nodes.elementAt(i); if( to.dummy() ) continue; Vector inedges = to.inedges(); int inedgecnt = inedges.size(); for (int j = 0; j < inedgecnt; ++j ) { Edge e = (Edge) inedges.elementAt(j); if( e.from().dummy() ) continue; boolean showit = (e.from().showing() && to.showing()); while( to.level() > e.from().level()+1 ) { Node from = e.from(); Node dummy = new Node( this ); // Make new node if( showit ) dummy.showing( true ); dummy.level( from.level()+1 ); // Set the level of the new node _nodes.addElement( dummy ); Edge inedge = new Edge( this, from, dummy ); // First new edge if( e.reversed() ) inedge.reverseDir(); from.replace_out( e, inedge ); dummy.add_in( inedge ); Edge outedge = new Edge( this, dummy, to ); // Second new edge if( e.reversed() ) outedge.reverseDir(); to.replace_in( e, outedge ); dummy.add_out( outedge ); _edges.addElement( inedge ); // Substitute new edges for old _edges.addElement( outedge ); _edges.removeElement( e ); e = outedge; } } } } protected final void removeDummiesDFS( Node curr, Node meta_root ) { curr.mark(); Vector inedges = curr.inedges(); int inedgecnt = inedges.size(); for (int i = 0; i < inedgecnt; ++i ) { Edge e = (Edge) inedges.elementAt(i); Node n = e.from(); if( n.dummy() ) { boolean reverse = e.reversed(); _edges.removeElement( e ); if( e.to() == meta_root ) continue; while( n.dummy() ) { _nodes.removeElement( n ); Edge other = (Edge) n.inedges().firstElement(); Node next = other.from(); _edges.removeElement( other ); if( !next.dummy() ) next.remove_out( other ); n = next; } Edge shortcut = new Edge( this, n, curr ); if( reverse ) shortcut.reverseDir(); n.add_out( shortcut ); curr.replace_in( e, shortcut ); _edges.addElement( shortcut ); } if( !n.marked() ) removeDummiesDFS( n, meta_root ); } Update(); } public synchronized void removeDummies() { if( !_hasDummies ) return; _hasDummies = false; unmarkNodes(); Node meta_root = makeMetaRoot(); removeDummiesDFS( meta_root, meta_root ); removeMetaRoot(); } // Localizaton and grouping stuff // public Vector get_fixed() { Vector fixed = new Vector(); int nodecnt = _nodes.size(); for (int i = 0; i < nodecnt; ++i ) { Node n = (Node) _nodes.elementAt(i); if( n.fixed() ) fixed.addElement( n ); } return fixed; } protected Node _center; protected void push_history( Node center, Vector ins, Vector outs ) { // Save the old "stack" Vector showing = new Vector(); Object v[] = new Object[6]; v[0] = _nodes.clone(); v[1] = _edges.clone(); v[2] = showing; v[3] = _center; v[4] = ins; v[5] = outs; _history.push( v ); int nodecnt = _nodes.size(); for (int i = 0; i < nodecnt; ++i ) { Node u = (Node) _nodes.elementAt(i); if( u.showing() ) showing.addElement( u ); u.center( false ); } _center = center; } protected void mark_local( Node center ) { // Mark all nodes within the right depth as showing center.mark(); int nodecnt = _nodes.size(); int edgecnt = _edges.size(); for( int depth = 0; depth < globals.localizationDepth(); ++depth ) { for( int i = 0; i < edgecnt; ++i ) { Edge e = (Edge) _edges.elementAt(i); Node from = e.from(); Node to = e.to(); if( from.marked() && !to.marked() ) // Use center as flag for next round to.center( true ); if( !from.marked() && to.marked() ) from.center( true); } for( int i = 0; i < nodecnt; ++i ) { Node n = (Node) _nodes.elementAt(i); if( n.center() ) { n.center( false ); n.mark(); } } } // Finish marking all the dummy-studded edges which are partially marked for (int i = 0; i < edgecnt; ++i ) { Edge e = (Edge) _edges.elementAt(i); Node from = e.from(); Node to = e.to(); if( from.dummy() && from.marked() && !to.marked() ) { while( to.dummy() ) { to.mark(); to = ((Edge) to.outedges().firstElement()).to(); } to.mark(); } else if( to.dummy() && to.marked() && !from.marked() ) { while( from.dummy() ) { from.mark(); from = ((Edge) from.inedges().firstElement()).from(); } from.mark(); } } } public void only_show_marked() { int nodecnt = _nodes.size(); for( int i = 0; i < nodecnt; ++i ) { Node u = (Node) _nodes.elementAt(i); if( u.marked() ) u.showing( true ); else u.showing( false ); } } public synchronized void localize( Node center ) { push_history( center, null, null ); unmarkNodes(); mark_local( center ); only_show_marked(); center.center( true ); } public synchronized void group( Node center ) { Node grouped = new Node( this, "*" + center.label() + "*" ); grouped.showing( true ); grouped.x( center.x() ); grouped.y( center.y() ); grouped.z( center.z() ); Vector ins = new Vector(); Vector outs = new Vector(); push_history( grouped, ins, outs ); unmarkNodes(); Vector fixed = get_fixed(); if( fixed.size() == 1 ) { mark_local( center ); } else { int nodecnt = _nodes.size(); for (int i = 0; i < nodecnt; ++i ) { Node n = (Node) _nodes.elementAt( i ); if( n.fixed() ) { n.mark(); n.fix(false); } } } grouped.mark(); // Make _nodes contain only unmarked nodes, complement the set of marked. Vector group_complement = new Vector(); int nodecnt = _nodes.size(); for (int i = 0; i < nodecnt; ++i ) { Node n = (Node) _nodes.elementAt( i ); if( n.marked() ) n.unmark(); else { group_complement.addElement( n ); n.mark(); } } _nodes = group_complement; _nodes.addElement( grouped ); grouped.mark(); // Add all the relevant edges to the set of edges, modify if neccessary. Vector new_edges = new Vector(); Vector seen_in_nodes = new Vector(); Vector seen_out_nodes = new Vector(); int edgecnt = _edges.size(); for (int i = 0; i < edgecnt; ++i ) { Edge e = (Edge) _edges.elementAt(i); Node from = e.from(); Node to = e.to(); if( from.marked() && to.marked() ) new_edges.addElement( e ); else if( from.marked() && !to.marked() ) { ins.addElement( e ); if( seen_in_nodes.contains( from ) ) { from.remove_out( e ); } else { seen_in_nodes.addElement( from ); Edge newin = new Edge( this, from, grouped ); grouped.add_in( newin ); from.replace_out( e, newin ); new_edges.addElement( newin ); } } else if( to.marked() && !from.marked() ) { outs.addElement( e ); if( seen_out_nodes.contains( to ) ) { to.remove_in( e ); } else { seen_out_nodes.addElement( to ); Edge newout = new Edge( this, grouped, to ); grouped.add_out( newout ); to.replace_in( e, newout ); new_edges.addElement( newout ); } } } _edges = new_edges; grouped.center( true ); } public synchronized void backtrack() { if( _history.empty() ) return; Object v[] = (Object[]) _history.pop(); _nodes = (Vector) v[0]; _edges = (Vector) v[1]; Vector showing = (Vector) v[2]; Node new_center = (Node) v[3]; Vector ins = (Vector) v[4]; Vector outs = (Vector) v[5]; int nodecnt = _nodes.size(); // Initialize nodes for (int i = 0; i < nodecnt; ++i ) { Node u = (Node) _nodes.elementAt(i); u.showing( false ); u.center( false ); } int showcnt = showing.size(); // Show old showing nodes for (int i = 0; i < showcnt; ++i ) ((Node) showing.elementAt(i)).showing( true ); if( new_center != null ) // Mark the old center as center new_center.center( true ); // Deal with removing the grouping, if any if( ins != null ) { Vector ingrp = _center.inedges(); // Remove the edges to group center int ingrpcnt = ingrp.size(); for( int i = 0; i < ingrpcnt; ++i ) { Edge e = (Edge) ingrp.elementAt(i); e.from().remove_out( e ); } Vector outgrp = _center.outedges(); int outgrpcnt = outgrp.size(); for( int i = 0; i < outgrpcnt; ++i ) { Edge e = (Edge) outgrp.elementAt(i); e.to().remove_in( e ); } int inscnt = ins.size(); // Add the original edges back in for( int i = 0; i < inscnt; ++i ) { Edge e = (Edge) ins.elementAt(i); e.from().add_out( e ); } int outscnt = outs.size(); for( int i = 0; i < outscnt; ++i ) { Edge e = (Edge) outs.elementAt(i); e.to().add_in( e ); } } _center = new_center; } // Embedder stuff // protected Embedder _embedder; protected Hashtable _embedders; public Embedder embedder() { return _embedder; } public synchronized void embedder( Embedder e ) { _embedder = e; } public void addEmbedder( String name, Embedder embedder ) { _embedders.put( name, embedder ); } private boolean _embedderChanged = true; public void setEmbedding( String name ) { _embedder = (Embedder) _embedders.get(name); _embedderChanged = true; } public boolean catchEmbedderChange() { if( _embedderChanged ) { _embedderChanged = false; return true; } return false; } public void Init() { _embedder.Init(); double k = globals.k(); } public synchronized void Update() { int edgecnt = _edges.size(); for (int i = 0; i < edgecnt; ++i ) { Edge e = (Edge) _edges.elementAt(i); e.updateLength(); } } // Size stuff // protected double _lx; protected double _ly; protected double _ux; protected double _uy; public final double lx() { return _lx; } public final double ly() { return _ly; } public final double ux() { return _ux; } public final double uy() { return _uy; } public void setArea(double lx, double ly, double ux, double uy ) { _lx = lx; _ly = ly; _ux = ux; _uy = uy; } // Some debug stuff // public synchronized void printDebug() { int realminx = 0; int realmaxx = 0; int realminy = 0; int realmaxy = 0; int nodecnt = _nodes.size(); for (int i = 0; i < nodecnt; ++i ) { Node n = (Node) _nodes.elementAt(i); if( n.x() > realmaxx ) realmaxx = (int)(n.x()); else if( n.x() < realminx ) realminx = (int)(n.x()); if( n.y() > realmaxy ) realmaxy = (int)(n.y()); else if( n.y() < realminy ) realminy = (int)(n.y()); System.err.println( n.toString() ); } int edgecnt = _edges.size(); for (int i = 0; i < edgecnt; ++i ) { Edge e = (Edge) _edges.elementAt(i); System.err.println( e.toString() ); } System.err.println("lx:\t" + _lx); System.err.println("ly:\t" + _ly); System.err.println("ux:\t" + _ux); System.err.println("uy:\t" + _uy); System.err.println("D:\t" + globals.D()); System.err.println("L:\t" + globals.L()); System.err.println("realminx:\t" + realminx); System.err.println("realmaxx:\t" + realmaxx); System.err.println("realminy:\t" + realminy); System.err.println("realmaxy:\t" + realmaxy); if( _applet.panel() != null ) { System.err.println("panelminx:\t" + _applet.panel().minx()); System.err.println("panelmaxx:\t" + _applet.panel().maxx()); System.err.println("panelminy:\t" + _applet.panel().miny()); System.err.println("panelmaxy:\t" + _applet.panel().maxy()); System.err.println("panelwidth:\t" + _applet.panel().size().width); System.err.println("panelheight:\t" + _applet.panel().size().height); } System.err.println("depth3D:\t" + globals.depth3D() ); } } // class Blackboard