An undirected graph that keeps track of connected components (groups). : Geometry « 2D Graphics GUI « Java






An undirected graph that keeps track of connected components (groups).

        
/**
 * Copyright (c) 2008-2010  Morten Silcowitz.
 *
 * This file is part of the Jinngine physics library
 *
 * Jinngine is published under the GPL license, available 
 * at http://www.gnu.org/copyleft/gpl.html. 
 */
//package jinngine.util;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;

/**
 * An undirected graph that keeps track of connected components (groups). Each
 * time an edge is added or removed from the graph, data structures are
 * maintained, reflecting connected components in the graph. This means, that
 * adding edges are roughly an O(k) operation, while removing an edge could i
 * result in a total traversal of the graph, visiting all present edges, worst
 * case O((n-1)^2) where n is the number of nodes. Usually this will be much
 * cheaper, given that the graph has a low density, and is fragmented into
 * separated components.
 * 
 * @param <T>
 *            Type that stores in nodes
 * @param <U>
 *            Type that stores in edges
 */
public class HashMapComponentGraph<T, U, V> implements ComponentGraph<T, U, V> {

  // wrapping classes
  private final class Node {
    public Node(T element) {
      this.element = element;
    }

    public final T element;
    public int color;

    public final int hashCode() {
      return element.hashCode();
    }

    public final boolean equals(Object other) {
      return element.equals(((Node) other).element);
    }
  }

  // component wrapper class for the type V.
  private final class Component {
    private final V element;

    public Component(V element) {
      this.element = element;
    }

    public final int hashCode() {
      return element.hashCode();
    }

    public final boolean equals(Object other) {
      return element.equals(((Component) other).element);
    }
  }

  // /**
  // * Node classifier for the ContactGraph
  // *
  // * @param <T> Type that stores in nodes
  // */
  // public interface NodeClassifier<T> {
  // /**
  // * @param node Node to classify
  // * @return true if the node is to be considered as a delimiting node, such
  // that two
  // * components in some graph, would not be merged if connected through such
  // a node. Returns false otherwise.
  // */
  // public boolean isDelimitor(final T node);
  // }

  // this would ideally be a Set, but the HashSet implementation doesn't allow
  // one to get the
  // actual reference to a specific object in the set. This means that we
  // can't keep our Node objects
  // unique, which we would like to do
  private final LinkedHashMap<Node, Node> allnodes = new LinkedHashMap<Node, Node>();
  private final LinkedHashSet<Node> freenodes = new LinkedHashSet<Node>();//
  private final Map<Node, Set<Node>> edges = new LinkedHashMap<Node, Set<Node>>();
  private final Map<Node, Component> component = new LinkedHashMap<Node, Component>();
  private final Map<Pair<T>, U> edgeData = new LinkedHashMap<Pair<T>, U>();//
  private final Map<Component, Set<Node>> componentNodes = new LinkedHashMap<Component, Set<Node>>();//
  private final Map<Component, Set<Pair<T>>> componentEdges = new LinkedHashMap<Component, Set<Pair<T>>>();//

  private final NodeClassifier<T> nodeClassifier;

  // TODO the nodes map is not cleaned up, nodes that are removed still
  // remains in here

  // default component creator
  private final ComponentHandler<T, V> componenthandler;

  // = new ComponentHandler<V>() {
  // public V newComponent() {
  // return null;
  // };
  // };

  // /**
  // * Create a new component graph
  // * @param nodeClassifier a classifier for the type T, used for the
  // connected components analysis
  // */
  // public HashMapComponentGraph( NodeClassifier<T> nodeClassifier ) {
  // this.nodeClassifier = nodeClassifier;
  // }

  /**
   * Create a new component graph
   * 
   * @param nodeClassifier
   *            a classifier for the type T, used for the connected components
   *            analysis
   * @param componetcreator
   *            a creator for new components that arrise inside the component
   *            graph
   */
  public HashMapComponentGraph(NodeClassifier<T> nodeClassifier,
      ComponentHandler<T, V> componentcreator) {
    this.componenthandler = componentcreator;
    this.nodeClassifier = nodeClassifier;
  }

  /**
   * Add an edge to the graph, and implicitly add included end-nodes if not
   * already present in the graph. This is roughly an O(k) and sometimes
   * O(nodes) operation, depending on whether components are to be merged or
   * not.
   * 
   * @param pair
   *            A pair of nodes, where an edge is to be added between them.
   * @param edgeelement
   *            An element of type U to store in the new edge
   */
  @Override
  public final void addEdge(Pair<T> pair, U edgeelement) {
    // do not act if edge is already present
    if (edgeData.containsKey(pair)) {
      // update the edge data user reference
      edgeData.put(pair, edgeelement);
      // System.out.println("Edge already present");
      return;
    }

    // add the new edge data to the edge
    edgeData.put(pair, edgeelement);

    // get nodes from the node tables ( in this way we can keep data other
    // than T in the Node objects)
    // if the nodes are not present, we add them to the allnodes and to
    // freenodes
    Node a = new Node(pair.getFirst());
    if (allnodes.containsKey(a)) {
      a = allnodes.get(a);
    } else {
      allnodes.put(a, a);
      freenodes.add(a);
    }
    Node b = new Node(pair.getSecond());
    if (allnodes.containsKey(b)) {
      b = allnodes.get(b);
    } else {
      allnodes.put(b, b);
      freenodes.add(b);
    }

    // if b is fixed, interchange a and b ( now, if b is fixed, both a and b
    // are fixed)
    if (nodeClassifier.isDelimitor(b.element)) {
      Node t = a;
      a = b;
      b = t;
    }

    // add edge to nodes. First create hash sets, and then add the nodes to
    // them
    if (!edges.containsKey(a))
      edges.put(a, new LinkedHashSet<Node>());
    if (!edges.containsKey(b))
      edges.put(b, new LinkedHashSet<Node>());
    edges.get(b).add(a);
    edges.get(a).add(b);

    // Cases
    // i. Both nodes are delimiters
    // a) do nothing
    // ii. One node is delimiter:
    // a). non-delimiter is in a component
    // do nothing
    // b). non-delimiter is not in a component
    // create a new component for the non-delimiter node
    // iii. No node is delimiter:
    // a). no node is in a component
    // create a new component for both nodes
    // b). one node is in a component
    // add the new node to this component
    // c). both nodes are in a component
    // 1. the same component
    // do nothing
    // 2. different components
    // merge the one component into the other

    // case i a)
    // both a and b are delimitors
    if (nodeClassifier.isDelimitor(b.element)) {
      // do nothing

      // ii)
      // one is delimiter
    } else if (nodeClassifier.isDelimitor(a.element)) {
      // if b is not in a group, create a new one for b
      if (!component.containsKey(b)) {
        // case ii b)
        Component g = new Component(componenthandler.newComponent());

        // add b to the new group
        component.put(b, g);
        componentNodes.put(g, new LinkedHashSet<Node>());
        componentNodes.get(g).add(b);

        // notify handler
        componenthandler.nodeAddedToComponent(g.element, b.element);

        // b is not a free node anymore
        freenodes.remove(b);

        // add to pairs
        componentEdges.put(g, new LinkedHashSet<Pair<T>>());
        componentEdges.get(g).add(pair);

        // return;
      } else {
        // case ii a)
        // add to pairs
        Component g = component.get(b);
        componentEdges.get(g).add(pair);
      }
      // non of the bodies are delimiters
    } else {

      // if b is in a group, interchange a and b
      // ( now, if b is in a group, both a and b are grouped)
      if (component.containsKey(b)) {
        Node t = a;
        a = b;
        b = t;
      }

      // both in components
      if (component.containsKey(b)) {
        // same component
        if (component.get(a) == component.get(b)) {
          // do nothing
          // add edge to this component
          componentEdges.get(component.get(a)).add(pair);
          // different components
        } else {
          // two nodes from two different components was connected.
          // we then merge the two components into one

          // merge groups (remove the gb group)
          Component ga = component.get(a);
          Component gb = component.get(b);

          // call the user handler to say we are merging gb into ga
          componenthandler.mergeComponent(ga.element, gb.element);

          // update the component table, i.e. update bodies that was
          // tied to component gb, to ga
          Iterator<Node> i = componentNodes.get(gb).iterator();
          while (i.hasNext()) {
            Node body = i.next();
            component.put(body, ga);
          }

          // put nodes in group b into group a
          componentNodes.get(ga).addAll(componentNodes.get(gb));
          componentEdges.get(ga).addAll(componentEdges.get(gb));

          // also, add the new edge (pair)
          componentEdges.get(ga).add(pair);

          // remove the gb component from the component table
          componentNodes.remove(gb);
          componentEdges.remove(gb);

          // return;
        }
        // one group
      } else if (component.containsKey(a)) {
        // assign b to the group of a
        Component g = component.get(a);
        component.put(b, g);
        componentNodes.get(g).add(b);
        componentEdges.get(g).add(pair);

        // b is not a free node anymore
        freenodes.remove(b);

        // notify handler that b is added to the group of a
        componenthandler.nodeAddedToComponent(g.element, b.element);

        // return;
        // no groups
      } else {
        // create a new component for both bodies
        Component newGroup = new Component(
            componenthandler.newComponent());
        component.put(a, newGroup);
        component.put(b, newGroup);
        componentNodes.put(newGroup, new LinkedHashSet<Node>());
        componentNodes.get(newGroup).add(a);
        componentNodes.get(newGroup).add(b);

        // notify handler that a and b is added to the new component
        componenthandler.nodeAddedToComponent(newGroup.element,
            a.element);
        componenthandler.nodeAddedToComponent(newGroup.element,
            b.element);

        componentEdges.put(newGroup, new LinkedHashSet<Pair<T>>());
        componentEdges.get(newGroup).add(pair);

        // both a and b are not free now
        freenodes.remove(a);
        freenodes.remove(b);
        // return;
      }

    }

    // // System.out.println("After add: " + groups.keySet().size() +
    // " groups with " + group.size() + " bodies" );
    // Iterator<Component> groupiter = componentNodes.keySet().iterator();
    //
    // Set<Pair<T>> allpairs = new HashSet<Pair<T>>();
    // Set<Node> allnodes = new HashSet<Node>();
    // while(groupiter.hasNext()){
    // Component g = groupiter.next();
    // //System.out.println( "Group " + g + " : " + groupPairs.get(g).size()
    // + " pairs " );
    //
    // Iterator<Pair<T>> pairiter = componentEdges.get(g).iterator();
    // while (pairiter.hasNext()) {
    // Pair<T> thispair = pairiter.next();
    // //System.out.println( "    pair:"+thispair.hashCode());
    // if (allpairs.contains(thispair)) {
    // System.out.println("Duplicates!!!!");
    // System.exit(0);
    // }
    // allpairs.add(thispair);
    //
    // }
    //
    //
    // Iterator<Node> nodeiter = componentNodes.get(g).iterator();
    // while (nodeiter.hasNext()) {
    // Node node = nodeiter.next();
    // //System.out.println( "     Node:"+node);
    // if (allnodes.contains(node)) {
    // System.out.println("Duplicates!!!!");
    // System.exit(0);
    // }
    // allnodes.add(node);
    //
    // }
    //
    //
    //
    // }
  }

  /**
   * Remove an edge. If the removal results in one or more isolated nodes,
   * these will be removed from the graph implicitly.
   * 
   * For non-dense and relatively fragmented graphs, this operation will be
   * cheap. Otherwise, for dense and strongly connected graphs, the operation
   * could include a full traversal of the graph visiting all present edges,
   * resulting in an O((n-1)^2) operation, where n is the number of nodes in
   * the graph.
   * 
   * @param pair
   *            edge to be removed
   * @return true if the edge was actually removed, false if the edge did not
   *         exists before call.
   */
  @Override
  public final boolean removeEdge(Pair<T> pair) {
    // don't act if edge is not present
    if (!edgeData.containsKey(pair)) {
      // System.out.println("Edge NOT present");
      return false;
    } else {
      edgeData.remove(pair);
    }

    // get the node out of the node adjacency hash map ( at this point we
    // know that the nodes must
    // exist, because the edge exists
    Node a = new Node(pair.getFirst());
    if (allnodes.containsKey(a)) {
      a = allnodes.get(a);
    } else {
      // not possible
      throw new IllegalStateException(
          "ComponentGraph.removeEdge(): Node did not have an adjacency entry. ComponentGraph corrupted.");
    }

    Node b = new Node(pair.getSecond());
    if (allnodes.containsKey(b)) {
      b = allnodes.get(b);
    } else {
      // this is not possible
      throw new IllegalStateException(
          "ComponentGraph.removeEdge(): Node did not have an adjacency entry. ComponentGraph corrupted.");
    }

    // if b is fixed, interchange a and b ( now, if b is fixed, both a and b
    // are fixed)
    if (nodeClassifier.isDelimitor(b.element)) {
      Node t = a;
      a = b;
      b = t;
    }

    // remove references to each node, in each nodes
    // connected node sets
    edges.get(a).remove(b);
    edges.get(b).remove(a);

    // if no edges left in set, remove the set
    if (edges.get(a).isEmpty())
      edges.remove(a);

    // if no edges left in set, remove it
    if (edges.get(b).isEmpty())
      edges.remove(b);

    // Cases
    // i. Both node are delimiters
    // do nothing
    // ii. One node is delimiter:
    // a). non-delimiter is in a component
    // do nothing (node could now be alone in its component)
    // if node contains no other edges, delete it from its component
    // b). non-delimiter is not in a component (not possible)
    // do nothing/ report fatal error
    // iii. No node is delimiter:
    // a). no node is in a component (not possible)
    // do nothing/ error
    // b). one node is in a component (not possible)
    // do nothing
    // c). both nodes are in a component
    // 1. the same component
    // remove edge, traverse breadth-first from each node to determine
    // if component should be split.
    // 2. different components (not possible)
    // do nothing/ error

    // both nodes are fixed
    if (nodeClassifier.isDelimitor(b.element)) {
      // do nothing
      // return;
      // one is fixed
    } else if (nodeClassifier.isDelimitor(a.element)) {
      if (component.containsKey(b)) { // only possible option
        // System.out.println("One fixed node");
        Component g = component.get(b);

        // check for another edge on this node
        if (!edges.containsKey(b)) {
          // System.out.println("b did not have any edges");
          // remove the node from component
          component.remove(b);

          // notify handler
          componenthandler.nodeRemovedFromComponent(g.element,
              b.element);

          // b is now free
          freenodes.add(b);

          Set<Node> s = componentNodes.get(g);
          if (!s.remove(b)) {
            System.out.println("ALARM");
            System.exit(0);
          }

          // remove group if empty
          if (s.isEmpty()) {
            // System.out.println("groups entry removed");
            componentNodes.remove(g);
            // TODO notify handler
          } else {
            System.out.println("Group isn't empty, why??");
            // System.exit(0);

          }
        } else {
          // b has edges left, and is part of a group. Were done
        }

        // remove edge from component (even if b was not removed from
        // the group)
        Set<Pair<T>> sp = componentEdges.get(g);
        sp.remove(pair);
        // remove group if empty
        if (sp.isEmpty()) {
          // System.out.println("grouppair entry removed " + g );
          componentEdges.remove(g);
        }

      } else {
        throw new IllegalStateException(
            "HashMapComponentGraph.removeEdge(): A connected non-delimiter node was not in a component. ComponentGraph corrupted.");
      }
      // return;
      // none is fixed
    } else {

      // if b has edges, interchange a and b
      // ( now, if b has edges, both a and b have edges)
      if (edges.containsKey(b)) {
        Node t = a;
        a = b;
        b = t;
      }

      // both are in the same group (only possible option)
      Component oldgroup = component.get(a);

      if (oldgroup != component.get(b)) {
        System.out.println("Different groups??!");
        System.exit(0);
      }
      // both have edges
      if (edges.containsKey(b)) {
        final int NONE = 0;
        final int RED = 1;
        final int BLUE = 2;

        // clear node colors in entire group
        Iterator<Node> i = componentNodes.get(oldgroup).iterator();
        while (i.hasNext()) {
          i.next().color = NONE;
        }

        // perform breadth-first traversal,
        // to determine if group has become disjoint
        boolean disjoint = true;
        Queue<Node> queue = new LinkedList<Node>();
        Set<Pair<T>> blueEdges = new LinkedHashSet<Pair<T>>();
        a.color = RED;
        b.color = BLUE;
        queue.add(a);
        queue.add(b);

        // traverse
        while (!queue.isEmpty()) {
          Node node = queue.poll();

          // add nodes neighbors to queue
          Iterator<Node> neighbors = edges.get(node).iterator();
          while (neighbors.hasNext()) {
            Node neighbor = neighbors.next();

            // remember visited edges
            if (node.color == BLUE)
              blueEdges.add(new Pair<T>(node.element,
                  neighbor.element));

            if (nodeClassifier.isDelimitor(neighbor.element)) {
              // ignore fixed nodes
              continue;
            } else if (neighbor.color == NONE) {
              neighbor.color = node.color;
              queue.add(neighbor);
              continue;
            } else if (neighbor.color != node.color) {
              // group is connected
              disjoint = false;
              break;
            } else {
              // already visited
              continue;
            }
          } // while neighbors
        } // while queue

        // handle result of traversal
        if (disjoint) {
          // System.out.println("Splitting group");

          // new group
          Component newgroup = new Component(
              componenthandler.newComponent());

          Set<Node> blues = new LinkedHashSet<Node>();

          // find all blue nodes
          Iterator<Node> iter = componentNodes.get(oldgroup)
              .iterator();
          while (iter.hasNext()) {
            Node node = iter.next();
            if (node.color == BLUE) {
              blues.add(node);
              component.put(node, newgroup);
            }
          }

          // impossible
          if (blues.isEmpty()) {
            System.out.println("Why was no blue nodes found?");
            System.exit(0);
          }

          // remove bodies from old components and add the new
          // component
          componentNodes.get(oldgroup).removeAll(blues);
          componentNodes.put(newgroup, blues);

          // remove blue edges from the red group and create a new
          // group with pairs (ng)
          componentEdges.get(oldgroup).removeAll(blueEdges);
          componentEdges.get(oldgroup).remove(pair); // the edge that
                                // was to be
                                // removed
          componentEdges.put(newgroup, blueEdges);
          // return;

        } else {
          // System.out.println("Group still connected");
          // we keep group as it is, but remove the pair (edge)
          Set<Pair<T>> sp = componentEdges.get(oldgroup);
          sp.remove(pair);

          // remove group if empty
          if (sp.isEmpty()) {
            // System.out.println("grouppair entry removed " +
            // oldgroup );
            componentEdges.remove(oldgroup);
          }

          // return;
        }

        // a has an edge and b do not
      } else if (edges.containsKey(a)) {
        // keep group as it is, but wipe out b
        component.remove(b);
        componentNodes.get(oldgroup).remove(b);

        // b is now a free node
        freenodes.add(b);

        // notify handler that b is removed from oldgroup
        componenthandler.nodeRemovedFromComponent(oldgroup.element,
            b.element);

        if (componentNodes.get(oldgroup).isEmpty()) { // never happens
          System.out.println("How can group be empty?");
          componentNodes.remove(oldgroup);
        }

        // remove from pairs
        // System.out.println("removing " + pair +" from group pairs " +
        // oldgroup);
        Set<Pair<T>> sp = componentEdges.get(oldgroup);
        sp.remove(pair);
        // remove group if empty
        if (sp.isEmpty()) {
          // System.out.println("grouppair entry removed " + oldgroup
          // );
          componentEdges.remove(oldgroup);
        }

        // non have edges
      } else {
        // clear out group entirely
        component.remove(a);
        component.remove(b);

        // both a and b are free nodes now
        freenodes.add(a);
        freenodes.add(b);

        // notify handler that a and b is removed
        componenthandler.nodeRemovedFromComponent(oldgroup.element,
            a.element);
        componenthandler.nodeRemovedFromComponent(oldgroup.element,
            b.element);

        // assume that the group is only containing a and b?
        componentNodes.get(oldgroup).remove(b);
        componentNodes.get(oldgroup).remove(a);

        if (componentNodes.get(oldgroup).isEmpty()) {
          componentNodes.remove(oldgroup);
        } else { // impossible
          System.out
              .println("Hmm still stuff in group but no outgoing edges?"
                  + componentNodes.get(oldgroup)
                  + " a and b is " + a + ",    " + b);
          System.exit(0);
        }

        // remove from pairs
        Set<Pair<T>> sp = componentEdges.get(oldgroup);
        sp.remove(pair);
        // remove group if empty
        if (sp.isEmpty()) {
          // System.out.println("grouppair entry removed " + oldgroup
          // );
          componentEdges.remove(oldgroup);
        }

      }// non have edges
    } // none is fixed

    // System.out.println("After remove: " + groups.keySet().size() +
    // " groups with " + group.size() + " bodies" );
    // Iterator<Component<V>> groupiter =
    // componentNodes.keySet().iterator();
    //
    // Set<Pair<T>> allpairs = new HashSet<Pair<T>>();
    // Set<Node> allnodes = new HashSet<Node>();
    // while(groupiter.hasNext()){
    // Component<V> g = groupiter.next();
    // //System.out.println( "Group " + g + " : " + groupPairs.get(g).size()
    // + " pairs " );
    //
    // Iterator<Pair<T>> pairiter = componentEdges.get(g).iterator();
    // while (pairiter.hasNext()) {
    // Pair<T> thispair = pairiter.next();
    // //System.out.println( "    pair:"+thispair.hashCode());
    // if (allpairs.contains(thispair)) {
    // System.out.println("Duplicates!!!!");
    // System.exit(0);
    // }
    // allpairs.add(thispair);
    //
    // }
    //
    //
    // Iterator<Node> nodeiter = componentNodes.get(g).iterator();
    // while (nodeiter.hasNext()) {
    // Node node = nodeiter.next();
    // //System.out.println( "    Node:"+node);
    // if (allnodes.contains(node)) {
    // System.out.println("Duplicates!!!!");
    // System.exit(0);
    // }
    // allnodes.add(node);
    //
    // }
    //
    // }

    return true;
  }

  @Override
  public final U getEdge(Pair<T> pair) {
    if (edgeData.containsKey(pair)) {
      return edgeData.get(pair);
    } else {
      return null;
    }
  }

  @Override
  public final Iterator<V> getComponents() {
    // wrapping iterator
    return new Iterator<V>() {
      private final Iterator<Component> iter = componentEdges.keySet()
          .iterator();

      public boolean hasNext() {
        return iter.hasNext();
      }

      @Override
      public V next() {
        return iter.next().element;
      }

      @Override
      public void remove() {
        throw new UnsupportedOperationException();
      }
    };
  }

  @Override
  public final Iterator<U> getEdgesInComponent(V c) {
    // get edges from component
    final Set<Pair<T>> edges = componentEdges.get(new Component(c));

    // abort if the component doesn't exist
    if (edges == null)
      return null;

    // get the edges
    final Iterator<Pair<T>> i = edges.iterator();

    // create an iterator that wraps the process of picking out the
    // edge data types from edgeData
    return new Iterator<U>() {
      @Override
      public boolean hasNext() {
        return i.hasNext();
      }

      @Override
      public U next() {
        if (i.hasNext()) {
          Pair<T> p = i.next();
          // return the edge data
          return edgeData.get(p);
        }
        // no element available
        return null;
      }

      @Override
      public void remove() {
        throw new UnsupportedOperationException();
      }
    };
  }

  @Override
  public Iterator<T> getNodesInComponent(V c1) {
    // get edges from component
    final Set<Node> nodes = componentNodes.get(new Component(c1));

    // abort if the component doesn't exist
    if (nodes == null)
      return null;

    // get the edges
    final Iterator<Node> i = nodes.iterator();

    // create an iterator iterates the nodes, but return the T element value
    return new Iterator<T>() {
      @Override
      public boolean hasNext() {
        return i.hasNext();
      }

      @Override
      public T next() {
        if (i.hasNext()) {
          Node p = i.next();
          // return the node data
          return p.element;
        }
        // no element available
        return null;
      }

      @Override
      public void remove() {
        throw new UnsupportedOperationException();
      }
    };

  }

  /**
   * Auxiliary method to print the graph
   */
  public final void print() {
    System.out.println("Status: " + componentNodes.keySet().size()
        + " components with " + component.size() + " bodies, "
        + getNumberOfFreeNodes() + " free ");
    Iterator<Component> groupiter = componentNodes.keySet().iterator();

    Set<Pair<T>> allpairs = new LinkedHashSet<Pair<T>>();
    Set<Node> allnodes = new LinkedHashSet<Node>();
    while (groupiter.hasNext()) {
      Component g = groupiter.next();
      System.out.println("Group " + g.element + " : "
          + componentEdges.get(g).size() + " pairs, "
          + componentNodes.get(g).size() + " nodes ");
      Iterator<Pair<T>> pairiter = componentEdges.get(g).iterator();
      while (pairiter.hasNext()) {
        Pair<T> thispair = pairiter.next();
        // System.out.println( "    pair:"+thispair.hashCode());
        if (allpairs.contains(thispair)) {
          System.out.println("Duplicates!!!!");
          System.exit(0);
        }
        allpairs.add(thispair);

      }

      Iterator<Node> nodeiter = componentNodes.get(g).iterator();
      while (nodeiter.hasNext()) {
        Node node = nodeiter.next();
        // System.out.println( "    Node:"+node);
        if (allnodes.contains(node)) {
          System.out.println("Duplicates!!!!");
          System.exit(0);
        }
        allnodes.add(node);

      }
    }
  }

  @Override
  public int getNumberOfComponents() {
    // return the number of keys in the component-Nodes map
    return componentNodes.keySet().size();
  }

  @Override
  public Iterator<T> getConnectedNodes(final T node) {
    // create a wrap iterator
    return new Iterator<T>() {
      Iterator<Node> i = edges.get(new Node(node)).iterator();

      @Override
      public boolean hasNext() {
        return i.hasNext();
      }

      @Override
      public T next() {
        return i.next().element;
      }

      @Override
      public void remove() {
      }
    };
  }

  public void addNode(T nodeelement) {
    // check if we know about this node
    Node node = new Node(nodeelement);

    if (allnodes.containsKey(node)) {
      // ignore. Maybe give warning?
      System.out
          .println("HashMapComponentGraph.addNode(): Node is already in graph");
    } else {
      // add to both allnodes and freenodes
      allnodes.put(node, node);
      freenodes.add(node);
    }
  }

  public void removeNode(T nodeelement) {
    Node node = new Node(nodeelement);
    // check if node is in freenodes. If so, we don't have to do anything
    // but removing the node
    // from both freenodes and allnodes
    if (freenodes.contains(node)) {
      freenodes.remove(node);
      allnodes.remove(node);
      return;
    }

    // we now expect node to be part of a component, but we can now simply
    // remove all incident
    // edges to the node. After that, the node will be placed into
    // freenodes, and we can trivially
    // remove it

    // remove each incident edges. store edges in intermediate list to avoid
    // concurrent modification errors
    List<Pair<T>> edgesToRemove = new ArrayList<Pair<T>>();
    Iterator<T> neighbors = getConnectedNodes(node.element);
    while (neighbors.hasNext()) {
      Pair<T> nodepair = new Pair<T>(node.element, neighbors.next());
      edgesToRemove.add(nodepair);
    }
    for (Pair<T> edge : edgesToRemove)
      removeEdge(edge);

    // we can now remove the node
    if (freenodes.contains(node)) {
      freenodes.remove(node);
      allnodes.remove(node);
      return;
    } else {
      System.out
          .println("HashMapComponentGraph.removeNode(): Node was not free after removing its edges");
    }
  }

  @Override
  public int getNumberOfNodes() {
    return allnodes.size();
  }

  @Override
  public int getNumberOfFreeNodes() {
    return freenodes.size();
  }

  @Override
  public Iterator<T> getFreeNodes() {
    // wrapping iterator
    return new Iterator<T>() {
      Iterator<Node> i = freenodes.iterator();

      @Override
      public boolean hasNext() {
        return i.hasNext();
      }

      @Override
      public T next() {
        return i.next().element;
      }

      @Override
      public void remove() {
        throw new UnsupportedOperationException();
      }
    };
  }

  @Override
  public Iterator<U> getConnectedEdges(final T node) {
    if (edges.containsKey(new Node(node))) {
      // create a wrap iterator
      return new Iterator<U>() {
        Set<Node> outEdges = edges.get(new Node(node));
        Iterator<Node> i = outEdges.iterator();

        @Override
        public boolean hasNext() {
          return i.hasNext();
        }

        @Override
        public U next() {
          // create a edge pair from the node types, and return the
          // edge data
          return edgeData.get(new Pair<T>(node, i.next().element));
        }

        @Override
        public void remove() {
          throw new UnsupportedOperationException();
        }
      };
    } else {
      // create a dummy empty iterator
      return new Iterator<U>() {
        @Override
        public boolean hasNext() {
          return false;
        }

        @Override
        public U next() {
          return null;
        }

        @Override
        public void remove() {
          throw new UnsupportedOperationException();
        }

      };
    }
  }
}

/**
 * Copyright (c) 2008-2010 Morten Silcowitz.
 * 
 * This file is part of the Jinngine physics library
 * 
 * Jinngine is published under the GPL license, available at
 * http://www.gnu.org/copyleft/gpl.html.
 */
// package jinngine.util;

// import java.util.Iterator;

/**
 * An undirected graph that keeps track of connected components (groups). Each
 * time an edge is added or removed from the graph, data structures are
 * maintained, reflecting connected components in the graph. This means, that
 * adding edges are roughly an O(k) operation, while removing an edge could i
 * result in a total traversal of the graph, visiting all present edges, worst
 * case O((n-1)^2) where n is the number of nodes. Usually this will be much
 * cheaper, given that the graph has a low density, and is fragmented into
 * separated components.
 * 
 * @param <T>
 *            Type that stores in nodes
 * @param <U>
 *            Type that stores in edges
 * @param <V>
 *            Type that stores in components
 */
interface ComponentGraph<T, U, V> {

  /**
   * Interface for supplying custom component objects. Components are used to
   * reference the independently connected components in the graph. It is
   * therefore useful to be able to store user information within the
   * component references.
   */
  public interface ComponentHandler<T, V> {
    /**
     * Called when a new component is created. This call MUST return a
     * unique object of type V, that has not been previously known to the
     * ComponentGraph
     * 
     * @return a new unique component of type V
     */
    public V newComponent();

    /**
     * Called prior to two components being merged. This gives the user a
     * way to manipulate data in the component type V. During this call, it
     * is possible to access the data of the ComponentGraph in a read-only
     * fashion.
     * 
     * @param remaining
     *            The component that will be the union of both components
     *            after merge
     * @param disappearing
     *            The component that will be removed from the graph
     */
    public void mergeComponent(V remaining, V leaving);

    /**
     * Called when a node is added to some component
     */
    public void nodeAddedToComponent(V component, T node);

    /**
     * Called when a node is removed from some component
     */
    public void nodeRemovedFromComponent(V component, T node);

  }

  /**
   * Node classifier for the ContactGraph
   * 
   * @param <T>
   *            Type that stores in nodes
   */
  public interface NodeClassifier<T> {
    /**
     * @param node
     *            Node to classify
     * @return true if the node is to be considered as a delimiting node,
     *         such that two components in some graph, would not be merged
     *         if connected through such a node. Returns false otherwise.
     */
    public boolean isDelimitor(final T node);
  }

  /**
   * Add a node to the graph. If the node already exists in the graph, the
   * call will have no effect.
   * 
   * @param node
   */
  public void addNode(T node);

  /**
   * Remove a node from the graph. All edges incident to this node will be
   * removed as well.
   * 
   * @param node
   */
  public void removeNode(T node);

  /**
   * Add an edge to the graph, and implicitly add included end-nodes if not
   * already present in the graph. This is roughly an O(k) and sometimes
   * O(nodes) operation, depending on whether components are to be merged or
   * not.
   * 
   * @param pair
   *            A pair of nodes, where an edge is to be added between them.
   * @param edgeelement
   *            An element of type U to store in the new edge
   */
  public void addEdge(Pair<T> pair, U edgeelement);

  /**
   * Remove an edge. If the removal results in one or more isolated nodes,
   * these will be removed from the graph implicitly.
   * 
   * For non-dense and relatively fragmented graphs, this operation will be
   * cheap. Otherwise, for dense and strongly connected graphs, the operation
   * could include a full traversal of the graph visiting all present edges,
   * resulting in an O((n-1)^2) operation, where n is the number of nodes in
   * the graph.
   * 
   * @param pair
   *            edge to be removed
   * @return true if the edge was actually removed, false if the edge did not
   *         exists before call.
   */
  public boolean removeEdge(Pair<T> pair);

  /**
   * Get the edge element of type U that is stored in the edge defined by a
   * pair of node types T. If no such edge exist, the return value is null.
   * 
   * @param pair
   *            A pair of T type objects defining an edge in the graph
   * @return The U type object stored in the edge. Return value is null if no
   *         such edge is present in the graph
   */
  public U getEdge(Pair<T> pair);

  /**
   * Return an iterator yielding the edges in the specified component.
   * 
   * @param c
   *            Component to iterate
   * @return Iterator giving the edge elements in the component
   */
  public Iterator<U> getEdgesInComponent(V c);

  /**
   * Returns an iterator yielding the nodes present in the given component
   * 
   * @param c
   *            Any component of this graph
   * @return An iterator yielding the nodes present in the component c
   */
  public Iterator<T> getNodesInComponent(V c);

  /**
   * Return an iterator that yields the components in the graph
   * 
   * @return
   */
  public Iterator<V> getComponents();

  /**
   * Return the number of components in this graph
   */
  public int getNumberOfComponents();

  /**
   * Return the total number of nodes in this graph
   */
  public int getNumberOfNodes();

  /**
   * Return the number of free nodes, which are nodes that are not a part of a
   * graph component
   */
  public int getNumberOfFreeNodes();

  /**
   * Get all free nodes. A free node is not in any component.
   */
  public Iterator<T> getFreeNodes();

  /**
   * Get all nodes that is connected to the given node. The constructible
   * pairs Pair<T> can then be used to obtain the edge type U using
   * getEdge(Pair<T>)
   */
  public Iterator<T> getConnectedNodes(T node);

  /**
   * Get all edges connected to the given node
   * 
   * @param node
   * @return An iterator over all edges connected to the given node
   */
  public Iterator<U> getConnectedEdges(T node);

}

/**
 * Copyright (c) 2008-2010 Morten Silcowitz.
 * 
 * This file is part of the Jinngine physics library
 * 
 * Jinngine is published under the GPL license, available at
 * http://www.gnu.org/copyleft/gpl.html.
 */
// package jinngine.util;

/**
 * Small pair class, for the purpose of indexing unordered pairs in a hash table
 */
final class Pair<T> {
  private final T o1;
  private final T o2;

  public Pair(T o1, T o2) {
    this.o1 = o1;
    this.o2 = o2;
  }

  // since this is an unordered pair, we use
  // the same hash code for interchanced objects
  @Override
  public final int hashCode() {
    return o1.hashCode() * o2.hashCode();
  }

  @SuppressWarnings("unchecked")
  @Override
  public final boolean equals(Object other) {
    if (this == other)
      return true;
    if (other == null)
      return false;
    if (other instanceof Pair) {
      final Pair<T> otherpair = (Pair<T>) other;
      return ((o1.equals(otherpair.o1) && o2.equals(otherpair.o2)) || (o1
          .equals(otherpair.o2) && o2.equals(otherpair.o1)));
    } else {
      return false;
    }
  }

  public final T getFirst() {
    return o1;
  }

  public final T getSecond() {
    return o2;
  }

  public final boolean contains(T o) {
    return (o == o1 || o == o2);
  }

}

   
    
    
    
    
    
    
    
  








Related examples in the same category

1.Collection of geometry utility methods
2.Unions Rectangle2D
3.Interpolates points given in the 2D plane
4.Returns distance between two sets of coords
5.Returns distance between 3D set of coords
6.Returns closest point on segment to point
7.Calculate Angle From
8.Returns distance to segment
9.Hexagon demo
10.Implements an Vector in 3D space.
11.Implementation of the 4 dimensional vector.
12.Quaternion
13.Circle shape
14.Geometry Utilities
15.This is a Polygon that allows the user to flip and swap the points along it's axis.
16.Fast trigonometric operationsFast trigonometric operations
17.A class to represent a latitude and longitude
18.Generates n logarithmically-spaced points between d1 and d2 using the provided base.
19.Returns a dimension where width and height are inside the bounds of the maxWidth and maxHeight parameters